;***************************************************************************
; ;
; EEEEEEEEEE OOOOOOO SSSSSSSS 5555555555 ;
; EE OO OO SS SS 55 ;
; EE OO OO SS 55 ;
; EEEEEEEE OO OO SSSSSSSS XXXXXX 555555555 ;
; EE OO OO SS 55 ;
; EE OO OO SS 55 ;
; EE OO OO SS 55 ;
; EE OO OO SS SS 55 55 ;
; EEEEEEEEEE OOOOOOO SSSSSSSS 55555555 ;
; ;
;***************************************************************************
;EOS-5 COMMENTED ASSEMBLY LISTING.
;VERSION HISTORY
; 2 9908.17 Richard F. Drushel Fixed description of PCB/DCB-
; wiping code in HARD_RESET
; 1 9508.08 Richard F. Drushel Fixed some comments in light of
; Coleco tech manual. Renamed all
; *public* symbols with Coleco
; names from EOS6 source code.
; Added caveats and disclaimers.
; 0 9208.10 Richard F. Drushel Original code regenerated from
; personal commented disassembly
; listing 8808.xx. Binary code
; verified identical to ROM code
; in both R59 and R80 ADAMs.
; Labels reflect absolute address
; locations of code. Z80-6801
; synch errors and total ADAMnet
; device list from "ADAM Technical
; Manual" (Coleco, 1984). Some
; comments need to be changed to
; reflect info in the tech manual.
;***************************************************************************
;Source code regenerated 1992 by Richard F. Drushel
;Comments (c) 1988, 1992, 1995, 1999 by Richard F. Drushel
;All rights reserved.
;Source code formatted for Z80ASM+ assembler (SLR)
;***************************************************************************
;Some notes on the proper usage of this assembly listing.
; This regenerated source code for EOS-5 represents a huge investment
;of time and intellectual effort on my part. It forms the basis for all
;my operating systems development for the Coleco ADAM. Writing all the
;comments, with only the partial descriptions available in "The Hacker's
;Guide to ADAM" books (some of them patently *wrong*), taught me how (and
;how *not*) to write Z80 code. I'm somewhat reluctant to let it out in
;this easy-to-misuse form. But I'm doing it nonetheless. The ADAM community
;needs technical information readily available, and everything else is out
;of print (including the ADAM Technical Manual with its source listings
;of EOS-6 and OS-7).
; For this courtesy, I'd ask a few favors in return. They'll make me
;feel better, they'll help you become a better programmer, and they'll
;make my job of supporting *your* software easier under the "new" EOS I've
;been working on since 1992, and which has had a partial debut in the
;ADAMserve Serially-Linked Device Protocol.
; (1) Respect the integrity of EOS. Operating systems have defined
;entrances and defined exits, but what goes on in between is a "black box"
;that you're not supposed to fool around in. Read, memorize, and accept
;as your programming religion the following paragraph from the first page
;of Coleco's EOS-6 source code:
; This absolute listing was generated to ease software
; development on ADAM. This listing provides the location
; of both released and unreleased entry points. Released
; entry points begin immediately in this file with the jump
; table and end before the first code segment listed.
; Released entry points include the jump table, common data
; areas (EOS_COMN), common data tables, and equates which
; describe the released data structures. Direct access
; to code segments is STRONGLY DISCOURAGED and may make
; your application incompatible with some ADAMs. There is
; more than one version of EOS on the market at this time
; and updates are planned.
;This means: *ALWAYS* go through the jump table. Except for the
;actual internal entry points from the jump table (e.g. __READ_BLOCK),
;all other internal labels in my code have the form Annnnn, where nnnnn
;is a number 0-65535. These are not the Coleco names for these internal
;references, but I've left them in my raw disassembly form, to clearly
;distinguish them from public symbols. There is no guarantee that these
;internal symbols will have the same value in a future version of EOS;
;the public symbols will *always* be the same.
; (2) Don't write directly to the hardware. Use EOS function calls
;to set the video mode and do device I/O. Don't access the VDP at the
;port I/O level. Don't write directly to DCBs to do ADAMnet device I/O.
;If your code bypasses EOS, it can't be supported by an ADAM emulator
;without enormous effort.
; (3) If you make your own changes to EOS, as a part of your own
;learning/hacking experience, please *don't* spread them around
;promiscuously. If you've found an improvement, by all means share
;it with the community; but let's try to keep some control over EOS
;versionitis. TDOS is bad enough :-)
; (4) If you have suggestions for improvement, please let me know.
;Be aware, however, that I've already done alot of the obvious things
;(replacing needless absolute JPs with JRs, importing shorter versions
;of file and device I/O from EOS-7, ADAMnet emulator code for non-
;ADAMnet hardware, EOS RAMdisk).
; (5) If you find bugs in my comments, I'd like to hear about them.
;I think I'm a little confused about some of the sound data structures,
;but even the EOS-6 comments are confusing.
; (6) Don't make any money off of this, either code or in publishing,
;without asking me first. The days of commercial gain from ADAM software
;are *over*, as far as I'm concerned.
; (7) Have *fun* with your ADAM!
;Richard F. Drushel, Ph.D.
;3353 Mayfield Road
;Cleveland Heights, Ohio 44118-1329 U.S.A.
;(216) 397-0684
;rfd@po.cwru.edu
;***************************************************************************
;Note: EOS function calls marked with an asterisk (*) indicate direct calls
;to the routine in memory, NOT through the jump table. This must have been
;done just to frustrate anyone who might try to disassemble it.
;***************************************************************************
EOS_CODE EQU 57344
ORG EOS_CODE
;***************************************************************************
;Externals and equates go here.
COLD_START_ADDR EQU 51200 ;boot block DTA
FCB_S EQU 54160 ;FCB0
THREE1K_BLKS EQU 54272 ;DTA0
;***************************************************************************
;EOS Function 78: WRITE VRAM.
; On entry, DE=VRAM target address to write, HL=RAM source address of
; data, BC=number of bytes to write.
__WRITE_VRAM:
PUSH BC ;save byte count
EX DE,HL ;HL=VRAM target address to write
CALL A57833 ;ENABLE VRAM WRITE subroutine
LD L,C ;L=VDP data port (190) returned by subrt
POP BC ;restore byte count
EX DE,HL ;HL=RAM source address, E=VDP data port
LD A,C ;A=lobyte of byte count
LD C,E ;C=VDP data port
LD D,B ;D=hibyte of byte count
INC D ;D=D+1 (needed for later DEC)
LD B,A ;B=lobyte of byte count (for OUTI)
OR A ;B=0? (even multiples of 256)
JR Z,A57366 ;YES, so get next 256
A57360:
OUTI ;send (HL) out (C), HL=HL+1, B=B-1
NOP
NOP ;wait a bit
JR NZ,A57360 ;B not zero, so keep sending data
A57366:
DEC D ;B=0, so one less unit of 256 to send
JR NZ,A57360 ;keep sending if D not zero
RET ;B and D both zero (count done), so EXIT
;***************************************************************************
;EOS Function 79: READ VRAM.
; On entry, DE=VRAM address to read, HL=RAM target address to receive
; data, BC=number of bytes to read.
__READ_VRAM:
PUSH BC ;save byte count
EX DE,HL ;HL=VRAM address to read
CALL A57831 ;ENABLE VRAM READ subroutine
LD L,C ;L=VDP data port (190) returned by subrt
POP BC ;restore byte count
EX DE,HL ;HL=RAM target address, E=VDP data port
LD A,C ;A=lobyte of byte count
LD C,E ;C=VDP data port
LD D,B ;D=hibyte of byte count
INC D ;D=D+1 (needed for later DEC)
LD B,A ;B=lobyte of byte count
OR A ;B=0? (even multiples of 256)
JR Z,A57392 ;YES, so get next 256
A57386:
INI ;read (C) into (HL), HL=HL+1, B=B-1
NOP
NOP ;wait a bit
JR NZ,A57386 ;B not zero, so keep reading data
A57392:
DEC D ;B=0, so one less unit of 256 to send
JR NZ,A57386 ;keep sending if D not zero
RET ;B and D both zero (count done), so EXIT
;***************************************************************************
;EOS Function 80: WRITE VDP REGISTER 0-7.
; On entry, B=register number to write (0-7), C=data byte to send. If
; register written was 0 or 1, the data byte sent is stored in RAM at
; 64865 (0) or 64866 (1).
__WRITE_REGISTER:
LD E,C ;E=data
LD A,(VDP_CTRL_PORT) ;A=VDP control port (191)
LD C,A
OUT (C),E ;send data out (191)
LD A,B ;A=register #
OR 128 ;set bit 7
OUT (C),A ;send register # out (191)
LD A,B ;A=register number
OR A ;was it register 0?
LD A,E ;A=data sent
JR NZ,A57417 ;NO, but was it register 1?
LD (VDP_REG_0),A ;YES, so save register 0 data in RAM
RET
;***************************************************************************
A57417:
DEC B ;
RET NZ ;EXIT if register 1 wasn't written
LD (VDP_REG_1),A ;save register 1 data in RAM
RET
;***************************************************************************
;EOS Function 81: READ VDP REGISTER 8.
__READ_REGISTER:
LD A,(VDP_CTRL_PORT) ;A=VDP control port (191)
LD C,A
IN A,(C) ;read port
LD (VDP_STATUS_BYTE),A ;save it in RAM
RET
;***************************************************************************
;EOS Function 82: FILL VRAM WITH 1 CHARACTER (in A).
; On entry, A=character to fill, DE=number of times to fill, HL=VRAM
; address to write.
__FILL_VRAM:
PUSH AF ;save character
CALL A57833 ;ENABLE VRAM WRITE subroutine
POP HL ;get back character in H
A57438:
OUT (C),H ;send char out VDP data port (from subrt)
DEC DE ;decrement counter
LD A,D
OR E ;is DE=zero?
JR NZ,A57438 ;NO, so keep sending
RET
;***************************************************************************
;EOS Function 83: INITIALIZE VRAM TABLE.
; On entry, A=code for which table to initialize: (0) sprite attribute
; table, (1) sprite generator table, (2) pattern name table, (3) pattern
; generator table, (4) color table. HL=VRAM address of table.
__INIT_TABLE:
LD C,A
LD B,0 ;BC=table code
LD IX,VRAM_ADDR_TABLE ;RAM table of pointers to VRAM tables
ADD IX,BC
ADD IX,BC ;offset code*2 into table
LD (IX+0),L
LD (IX+1),H ;store HL in RAM table
LD A,(VDP_REG_0) ;A=current VDP register 0
BIT 1,A ;test bit 1
JR Z,57509 ;set=graphics mode 2 (hires)
LD A,C ;clear=any other mode, so get table code
CP 3 ;pattern generator table?
JR Z,A57481 ;YES
CP 4 ;color table?
JR Z,A57495 ;YES
JR A57509 ;sprite attrib, gen or patt name tables
;***************************************************************************
A57481:
LD B,4 ;B=register 4 for Fn80 (PATTERN GENERATOR)
LD A,L
OR H ;is HL=zero?
JR NZ,A57491 ;NO
LD C,3 ;YES, so C=3=data to send for Fn80
JR A57531 ;write it
;***************************************************************************
A57491:
LD C,7 ;HL<>0, so C=7=data to send for Fn80
JR A57531 ;write it
;***************************************************************************
A57495:
LD B,3 ;B=register 3 for Fn80 (COLOR TABLE)
LD A,L
OR H ;is HL=zero?
JR NZ,A57505 ;NO
LD C,127 ;YES, so C=127=data to send for Fn80
JR A57531 ;write it
;***************************************************************************
A57505:
LD C,255 ;HL<>0, so C=255=data to send for Fn80
JR A57531 ;write it
;***************************************************************************
A57509:
LD IY,A57535 ;IY=base of data table
ADD IY,BC
ADD IY,BC ;offset 2*code # into table
LD A,(IY+0) ;A=table lobyte (# times to HL=HL/2)
LD B,(IY+1) ;B=table hibyte (reg to write in Fn80)
A57523:
SRL H
RR L ;HL=HL/2
DEC A ;decrement counter
JR NZ,A57523 ;not done yet, so keep dividing
LD C,L ;done, so C=L=data to send for Fn80
A57531:
CALL WRITE_REGISTER ;Fn80: WRITE VDP REGISTER 0-7
RET
;***************************************************************************
;DATA TABLE FOR Function 83: INITIALIZE VRAM TABLE.
; VRAM register divisor 2^n
A57535:
DB 7,5
;sprite attribute table 5 7 (128)
DB 11,6
;sprite generator table 6 11 (2048)
DB 10,2
;pattern name table 2 10 (1024)
DB 11,4
;pattern generator table 4 11 (2048)
DB 6,3
;color table 3 6 (64)
;***************************************************************************
;EOS Function 84: PUT TABLE TO VRAM.
; On entry, A=table code (see Fn83), HL=table address in RAM, DE=entry
; number in table, IY=number of entries to be moved.
__PUT_VRAM:
CALL A57557 ;TABLE OFFSET subroutine
JP WRITE_VRAM ;Fn78: WRITE VRAM
;***************************************************************************
;EOS Function 85: GET TABLE FROM VRAM.
; On entry, A=table code (see Fn83), HL=table address in RAM, DE=entry
; number in table, IY=number of entries to be moved.
__GET_VRAM:
CALL A57557 ;TABLE OFFSET subroutine
JP READ_VRAM ;Fn79: READ VRAM
;***************************************************************************
;TABLE OFFSET subroutine.
; On entry, A=table code (see Fn83), HL=table address in RAM, DE=entry
; number in table, IY=number of entries to be moved. On exit, HL is
; untouched, DE=offset into RAM table, BC=number of bytes to move.
A57557:
PUSH IY ;save # entries to move
LD C,A ;save table code
CP 4 ;color table?
JR NZ,A57572 ;NO
LD A,(VDP_REG_0) ;YES, so A=current register 0
AND 2 ;mask out all but bit 1
JR Z,A57591 ;bit 1 cleared
LD A,C ;bit 1 set=graphics mode 2; get table code
A57572:
CP 2 ;pattern name table?
JR Z,A57591 ;YES
EX DE,HL ;NO, so HL=entry number DE (orig. HL safe)
ADD HL,HL ;HL*2
ADD HL,HL ;HL*4
OR A ;sprite attribute table?
JR Z,A57583 ;YES, so no more offset
ADD HL,HL ;HL*8
A57583:
EX DE,HL ;result: DE=DE*4 or *8; HL untouched
EX (SP),HL ;since IY on stack, HL=IY, old HL on stack
ADD HL,HL ;HL*2
ADD HL,HL ;HL*4
JR Z,A57590 ;sprite attrib table? YES=no more offset
ADD HL,HL ;HL*8
A57590:
EX (SP),HL ;result: stack=IY*4 or *8, HL untouched
A57591:
LD A,C ;A=table code
LD BC,VRAM_ADDR_TABLE ;RAM table of pointers to VRAM tables
PUSH HL ;save original entry HL
LD H,0
LD L,A ;HL=table code
ADD HL,HL ;HL*2
ADD HL,BC ;plus base of RAM table=offset
LD A,(HL) ;A=lobyte of address in RAM table
INC HL ;point to hibyte
LD H,(HL) ;H=hibyte of address in RAM table
LD L,A ;HL=VRAM table address
ADD HL,DE ;add offset amount in DE computed above
EX DE,HL ;DE=offset address in VRAM
POP HL ;HL still=entry value (RAM source table)
POP BC ;BC from stack=entry IY*4 or *8
RET
;***************************************************************************
;EOS Function 86: CALCULATE OFFSET INTO SPRITE ATTRIBUTE TABLE.
; On entry, D=Y-coordinate of pattern position, E=X-coordinate. D and E
; are signed 8-bit numbers (-128 to +127). On exit, DE=(Y*32)+X.
__CALC_OFFSET:
PUSH HL
BIT 7,D ;D<0? (sign bit set)
JR Z,A57619 ;YES
LD H,255 ;NO, so H=255
JR A57621
;***************************************************************************
A57619:
LD H,0 ;D<0 signed, so H=0
A57621:
LD L,D ;HL=D (sign bit clear) or HL=65280+D (set)
ADD HL,HL ;HL*2
ADD HL,HL ;HL*4
ADD HL,HL ;HL*8
ADD HL,HL ;HL*16
ADD HL,HL ;HL*32; result is HL=D*32
BIT 7,E ;E<0? (sign bit set)
JR Z,A57635 ;YES
LD D,255 ;NO, so D=255
JR A57637
;***************************************************************************
A57635:
LD D,0 ;E<0 signed, so H=0
A57637:
ADD HL,DE ;result is HL=(D*32)+E
EX DE,HL ;DE=offset
POP HL
RET
;***************************************************************************
;EOS Function 87: POINT TO PATTERN POSITION.
; On entry, DE=signed 16-bit number (X-coordinate or Y-coordinate of
; pattern). On exit, DE ranges from -128 to +127.
__PX_TO_PTRN_POS:
PUSH HL
PUSH BC
LD B,3 ;number of times to (DE=DE/2)
A57645:
SRA D
RR E ;DE=DE/2
DJNZ A57645 ;keep dividing 'til B=0
POP BC
LD HL,65408 ;HL=1111 1111 1000 0000 binary
BIT 7,D ;is DE<0? (sign bit set)
JR NZ,A57665 ;NO
ADD HL,DE ;YES, so HL=HL+DE; was there a carry?
POP HL ;restore entry HL
RET NC ;no carry, so EXIT
LD E,127 ;carry, so DE=+127
RET
;***************************************************************************
A57665:
LD H,0 ;zero out sign extension in H
ADD HL,DE ;HL=HL+DE; was there a carry?
POP HL ;restore entry HL
RET C ;carry, so EXIT
LD E,128 ;no carry, so DE=-128
RET
;***************************************************************************
;EOS Function 88: LOAD ASCII CHARACTER SET FROM ROM TO VDP.
__LOAD_ASCII:
LD DE,(PATTRNGENTBL) ;DE=VRAM addr of pattern generator table
LD HL,0 ;HL=ASCII code of 1st character to load
LD BC,128 ;# of character patterns to load
;fall thru to Fn77: PUT ASCII CHAR TO VDP
;**************************************************************************
;EOS Function 77: PUT ASCII CHARACTER PATTERN TO VDP.
; On entry, HL=code of first character pattern to load, BC=number of
; patterns, DE=VRAM address of pattern generator table.
__PUT_ASCII:
ADD HL,HL ;HL*2
ADD HL,HL ;HL*4
ADD HL,HL ;HL*8 -- 8 bytes per pattern (offset)
PUSH BC ;save BC on stack
EX (SP),HL ;effectively, HL=BC, old HL on stack
ADD HL,HL ;HL*2
ADD HL,HL ;HL*4
ADD HL,HL ;HL*8 -- total bytes to load = # char*8
EX (SP),HL ;effectively, old HL back, BC*8 on stack
POP BC ;BC=BC*8 from stack
LD IX,0
ADD IX,SP ;IX=address of old stack
LD SP,TEMP_STACK ;address of temporary stack
PUSH IX ;save address of old stack
LD A,(CUR_BANK) ;A=current memory configuration
PUSH AF ;save it
LD A,(MEM_CNFG00) ;memory configuration 0: EOS -- RAM
PUSH BC ;save pattern count
CALL SWITCH_MEM ;Fn76: BANK SWITCH MEMORY (to A)
POP BC ;get back pattern count
PUSH DE ;save VRAM addr of pattern gen table
LD DE,(258) ;DE=addr of char set patterns in EOS ROM
ADD HL,DE ;add offset so HL=EOS addr of 1st pattern
POP DE ;restore VRAM addr of pattern gen table
CALL WRITE_VRAM ;Fn78: WRITE VRAM
POP AF ;get back original memory configuration
CALL SWITCH_MEM ;Fn76: BANK SWITCH MEMORY (to A)
POP HL ;get back old stack address
LD SP,HL ;point SP at old stack
RET
;***************************************************************************
;EOS Function 76: BANK SWITCH MEMORY (to A).
; On entry, A=memory configuration (0-15). Memory configurations decode
; as follows:
; lower 32K upper 32K
; 0 SmartWriter or EOS RAM
; 1 RAM RAM
; 2 expansion RAM RAM
; 3 OS-7 plus 24K RAM RAM
; 4 SmartWriter or EOS expansion ROM (center slot)
; 5 RAM expansion ROM (center slot)
; 6 expansion RAM expansion ROM (center slot)
; 7 OS-7 plus 24K RAM expansion ROM (center slot)
; 8 SmartWriter or EOS expansion RAM
; 9 RAM expansion RAM
;10 expansion RAM expansion RAM
;11 OS-7 plus 24K RAM expansion RAM
;12 SmartWriter or EOS cartridge ROM
;13 RAM cartridge ROM
;14 expansion RAM cartridge ROM
;15 OS-7 plus 24K RAM cartridge ROM
; In order to select the SmartWriter ROM, an OUT (63),0 must be executed
;first. To select the EOS ROM, use OUT (63),2.
__SWITCH_MEM:
LD B,A
LD A,(MEM_SWITCH_PORT) ;A=memory switch port (127)
LD C,A
OUT (C),B ;do the switch
LD A,B
LD (CUR_BANK),A ;save new memory configuration as current
RET
;***************************************************************************
;EOS Function 75: GET I/O PORTS FROM OS-7.
; The OS-7 ROM contains the ColecoVision game cartridge operating system.
; The values retrieved are stored in RAM as follows:
;64551 memory switch port 127
;64552 ADAMnet reset port 63
;64553 VDP control port 191
;64554 VDP data port 190
;64555 game controller 1 port 252
;64556 game controller 2 port 255
;64557 strobe set port 128
;64558 strobe reset port 192
;64559 sound port 255
__PORT_COLLECTION:
LD A,(CUR_BANK) ;A=current memory configuration
PUSH AF ;save it
LD A,(MEM_CNFG03) ;memory configuration 3:
;lower 32K=OS-7 plus 24K RAM
;upper 32K=RAM
CALL SWITCH_MEM ;Fn76: BANK SWITCH MEMORY (to A)
LD HL,VDP_CTRL_PORT ;HL=RAM address of VDP control port
LD A,(7491) ;get port from OS-7
LD (HL),A ;save it in RAM
INC HL ;HL=RAM address of VDP data port (64554)
LD A,(7495) ;get port from OS-7
LD (HL),A ;save it in RAM
INC HL ;HL=RAM addr of game contrl 1 port (64555)
LD A,(4427) ;get port from OS-7
LD (HL),A ;save it in RAM
INC HL ;HL=RAM addr of game contrl 2 port (64556)
LD A,(4433) ;get port from OS-7
LD (HL),A ;save it in RAM
INC HL ;HL=RAM address of strobe set port (64557)
LD A,(4439) ;get port from OS-7
LD (HL),A ;save it in RAM
INC HL ;HL=RAM addr of strobe reset port (64558)
LD A,(4456) ;get port from OS-7
LD (HL),A ;save it in RAM
INC HL ;HL=RAM address of sound port (64559)
LD A,(398) ;get port from OS-7
LD (HL),A ;save it in RAM
POP AF ;restore old memory configuration
CALL SWITCH_MEM ;Fn76: BANK SWITCH MEMORY (to A)
RET
;***************************************************************************
;EOS Function 89: WRITE VRAM SPRITE ATTRIBUTE TABLE.
; On entry, A=number of sprites to write, HL=address of sprite order table
; in RAM, DE=address of RAM copy of sprite attribute table.
__WR_SPR_ATTRIBUTE:
PUSH AF
PUSH HL
LD HL,(SPRITEATTRTBL) ;HL=VRAM addr of sprite attribute table
CALL A57833 ;ENABLE WRITE VRAM; return C=VDP data port
POP HL
POP AF
LD B,A ;save count of sprites to write
A57808:
LD A,(HL) ;A=sprite ID # from order table
ADD A,A ;A*2
ADD A,A ;A*4
INC HL ;point to next entry in order table
PUSH HL ;save address
LD L,A
LD H,0 ;HL=offset into table
ADD HL,DE ;DE=offset addr in RAM sprite attr table
LD A,B ;restore A=count of sprites to write
LD B,4 ;4 bytes to send per sprite
A57820:
OUTI ;send a byte at (HL) out VDP data port
NOP
NOP ;wait a bit
JR NZ,A57820 ;send the next byte
LD B,A ;restore B=count of sprites to write
POP HL ;HL=address of next entry in order table
DJNZ A57808 ;send the next sprite
RET ;all requested were sent; exit
;***************************************************************************
;ENABLE VRAM READ/WRITE subroutines.
; On entry, HL=VRAM address to read/write. On exit, C=VDP data port (190).
; Unfortunately, these subroutines have overlapping reading frames:
; (1) Function 79 (read VRAM) enters at 57831:
;57831 AF XOR A ;zero out A for reads
;57832 C23E40 JP NZ,16446 ;never jumps because ZF=1 after XOR A
;continues at 57835
; (2) Functions 78 (write VRAM), 82 (fill VRAM with 1 character (in A)),
; and 89 (write VRAM sprite attribute table) enter at 57833:
;57831 AFC2 ;unused
;57833 3E40 LD A,64 ;A=64 for writes; continues at 57835
A57831:
XOR A
DB 194
A57833:
LD A,64
LD BC,(VDP_CTRL_PORT) ;B=VDP data port, C=VDP control port
OUT (C),L ;send lobyte of VRAM address
OR H ;set bit 6 of H (writes) or not (reads)
OUT (C),A ;send hibyte of VRAM address
LD C,B ;VDP data port in C
RET
;***************************************************************************
;DATA TABLE FOR Function 90: READ GAME CONTROLLERS.
; Offset into the table is the actual data returned by the controller.
; Value at the offset is the decoded value.
; DATA IN: DECODED TO:
A57846:
DB 15 ; 0 15 = nothing
DB 6 ; 1 6
DB 1 ; 2 1
DB 3 ; 3 3
DB 9 ; 4 9
DB 0 ; 5 0
DB 10 ; 6 10 = *
DB 12 ; 7 12 = Super-Action controller button?
DB 2 ; 8 2
DB 11 ; 9 11 = #
DB 7 ; 10 7
DB 13 ; 11 13 = Super-Action controller button?
DB 5 ; 12 5
DB 4 ; 13 4
DB 8 ; 14 8
DB 15 ; 15 15 = nothing (this is an error)
;***************************************************************************
;READ AND DEBOUNCE GAME CONTROLLER subroutine.
; On entry, A=controller to read (0=player 2, 1=player 1). On exit, D=
; decoded keypad value (0-15), B=right button status (0=not pressed, 64=
; pressed), H=left button status (0=not pressed, 64=pressed), L=joystick
; position, E=previous value of spinner.
A57862:
LD C,A ;save controller number to read
LD A,I ;get current interrupt status register
;NOTE: parity flag set if INT disabled
PUSH AF ;save interrupt status
DI ;disable maskable INT (if not already)
LD A,C ;restore A=controller to read
LD BC,(STROBE_SET_PORT) ;B=strobe reset port, C=strobe set port
OUT (C),A ;read request
LD D,B ;save strobe reset port in D
LD HL,SPIN_SW0_CT ;HL=address of spinner 1
LD BC,(CONTROLLER_0_PORT) ;B=controller 2 port, C=controller 1 port
OR A ;are we reading controller 1 or 2?
JR NZ,A57887 ;#1, so reset spinner 1
INC HL ;#2, so point to spinner 2
LD C,B ;C=controller 2 port
A57887:
XOR A ;A=0
LD E,(HL) ;E=old spinner value
LD (HL),A ;new spinner value=0
LD B,D ;restore B=strobe reset port
IN A,(C) ;read controller port into A
CPL ;1's complement A
LD H,0
LD L,A ;HL=1's complement A
LD A,C ;save controller port in A
LD C,B ;C=saved strobe reset port
OUT (C),A ;debounce controller by sending the port #
LD C,A ;save controller port in C
POP AF ;restore entry flags
JP PO,A57907 ;at entry, maskable INTs were disabled,
;so leave them disabled
EI ;otherwise, enable interrupts
A57907:
LD A,L ;restore saved 1's complement of 1st read
PUSH AF ;save it again
AND 64 ;mask out all but bit 6
LD B,A ;save it in B (RIGHT BUTTON)
LD A,L ;restore saved 1's complement of 1st read
AND 15 ;select low nibble of byte (table offset)
LD L,A ;save it in L
PUSH DE
LD DE,A57846 ;DE=base of keypad decoding data table
ADD HL,DE ;point HL to data in table
POP DE
LD D,(HL) ;D=decoded value from table (KEYPAD)
IN A,(C) ;read controller port again
CPL ;1's complement A
PUSH AF ;save it
LD L,A ;put it in L
AND 64 ;mask out all but bit 6
LD H,A ;save it in H (LEFT BUTTON)
LD A,L ;restore saved 1's complement of A
AND 15 ;select low nibble of byte
LD L,A ;save it in L (JOYSTICK)
POP AF ;get 1's complement of 2nd read off stack
LD C,A ;put it in C
POP AF ;get 1's complement of 1st read off stack
RET ;exit
;***************************************************************************
;EOS Function 90: READ GAME CONTROLLERS.
; On entry, IX=address of 10-byte RAM table to hold controller data (joy-
; stick, left button, right button, decoded keypad, spinner for player 2,
; followed by player 1), A=weird code for which controller(s) to read:
; BITS 1,0:
; 00 none
; 01 controller 2
; 10 controller 1
; 11 controller 2, then controller 1
; BIT 7:
; 1 add old EOS spinner value to old in RAM data table
; 0 don't update RAM spinner
; NOTE: This routine must be called TWICE in succession with the same
; controller code in order to update the RAM table ONCE. See discussion
; under STORE CONTROLLER DATA IN RAM TABLE subroutine.
; Coleco programmer documentation for EOS-7 refers to CONTROLLER_0 and
; CONTROLLER_1. These in fact correspond to the ports which are marked "2"
; (player 2) and "1" (player 1) on the ADAM console, respectively. I will
; use the console/player designations.
__POLLER:
LD HL,PERSONAL_DEBOUNCE_TABLE ;HL=base of controller input data in EOS
LD C,A ;save controller read code in C
AND 1 ;is it controller 2?
JR Z,A57982 ;NO
DEC A ;YES...
CALL A57956 ;read controller 2
BIT 1,C ;do we also read controller 1?
RET Z ;NO (clear) so exit having read just #2
A57954:
LD A,1 ;YES so A=1 (READ/DEBOUNCE code for #1)
A57956:
PUSH BC ;resave controller read code
PUSH HL ;save EOS table position
CALL A57862 ;READ/DEBOUNCE GAME CONTROLLER subrt
LD C,H ;C=left button status
LD A,L ;A=joystick position
POP HL ;restore EOS table position
CALL A57995 ;STORE CONTROLLER DATA IN RAM subroutine
POP BC ;restore controller read code
BIT 7,C ;update RAM spinner with EOS data?
JR Z,A57979 ;NO, so exit
LD A,E ;YES, so A=old EOS spinner value
ADD A,(IX+0) ;add it to RAM spinner
LD (IX+0),A ;save new spinner in RAM table
A57979:
INC IX ;point to next RAM controller block
RET
;***************************************************************************
A57982:
BIT 1,C ;do we read controller 1?
RET Z ;NO (clear), so exit having read nothing
LD DE,4 ;YES (set), so skip over controller 2
ADD HL,DE ;HL=65118 (EOS data for controller 1)
INC DE ;length of RAM table entry=5
ADD IX,DE ;point IX to RAM table for controller 1
JP A57954 ;go back
;***************************************************************************
;STORE CONTROLLER DATA IN RAM TABLE subroutine.
; On entry, HL=address of EOS table for controller data (either player 2
; or player 1 4-byte block), IX=address of similar RAM table (but 5 bytes
; per player block, spinner last), A=joystick position, C=left button
; status, B=right button status, D=keypad status. On exit, IX points to
; spinner data byte. NOTE: This routine alternately updates either the
; EOS or the RAM table. The first call updates EOS, and the second
; compares the incoming data with that previously stored in EOS. The RAM
; table will only be updated if the two match. Therefore, Function 90
; must be called TWICE in succession in order to update the RAM table ONCE.
A57995:
CALL A58007 ;store joystick data (entry A) in RAM
LD A,C ;A=left button status
CALL A58007 ;store it
LD A,B ;A=right button status
CALL A58007 ;store it
LD A,D ;A=decoded keypad
A58007:
CP (HL) ;compare it with previous value in EOS
JR NZ,A58015 ;different, so update EOS but not RAM
LD (IX+0),A ;same, so update RAM and...
OR 128 ;set bit 7 of data (make it different)
A58015:
LD (HL),A ;update EOS
INC IX ;point to next EOS
INC HL ;point to next RAM
RET
;***************************************************************************
;EOS Function 91: UPDATE SPINNER 1 AND 2.
__UPDATE_SPINNER:
LD BC,(CONTROLLER_0_PORT) ;B=controller 2 port, C=controller 1 port
IN A,(C) ;read controller 1 port
LD HL,SPIN_SW0_CT ;HL=address of spinner 1
BIT 4,A ;was it spun? (bit 4 clear)
JR NZ,A58040 ;NO, so check spinner 2
AND 32 ;YES, but which way? (bit 5)
JR NZ,A58039 ;UP (bit 5 set) so increment spinner
DEC (HL) ;DOWN (bit 5 clear) so decrement spinner
DEC (HL) ;down 2 and up 1 equals down 1
A58039:
INC (HL)
A58040:
LD C,B ;C=controller 2 port
IN A,(C) ;read controller 2 port
INC HL ;HL=65113 (address of spinner 2)
BIT 4,A ;was it spun? (bit 4 clear)
RET NZ ;NO, so exit
AND 32 ;YES, but which way? (bit 5)
JR NZ,A58053 ;UP (bit 5 set) so increment spinner
DEC (HL) ;DOWN (bit 5 clear) so decrement spinner
RET
;****************************************************************************
A58053:
INC (HL) ;increment spinner
RET
;***************************************************************************
;UPDATE SIMPLE NOTE subroutine.
; On entry, IX=output table address. On exit, ZF=1 if the note is over,
; ZF=0 if not.
A58055:
LD A,(IX+7) ;A=freq step size byte from output table
OR A ;is it zero?
JR NZ,A58070 ;NO, so UPDATE FREQUENCY-SWEPT NOTE
LD A,(IX+5) ;YES, so A=note length byte
DEC A ;one less clock tick; is it zero?
RET Z ;YES, so exit
LD (IX+5),A ;NO, so save new length in output table
RET
;***************************************************************************
;UPDATE FREQUENCY-SWEPT NOTE subroutine.
; On entry, IX=output table address. On exit, ZF=1 if the note is over,
; ZF=0 if not.
A58070:
PUSH IX ;save output table address...
POP HL ;and get it back in HL
LD DE,6 ;offset
ADD HL,DE ;point to byte IX+6: freq step period (4
;bits), 1st freq step period (4 bits)
CALL __DECLSN ;*Fn92: DECREMENT LOW NIBBLE OF (HL)
;1st freq step period is decremented
RET NZ ;not time to step yet, so exit
CALL __MSNTOLSN ;*Fn94: HIGH NIBBLE OF (HL) TO LOW NIBBLE
;time to step! so move freq step period
;down to lower 4 bits (still in upper 4)
;all later freq steps have this duration
DEC HL ;back up to IX+5: note length
LD A,(HL) ;A=note length
DEC A ;one less clock tick; is it zero?
RET Z ;YES, so exit
LD (HL),A ;NO, so save new length in output table
DEC HL
DEC HL ;back up 2 to IX+3: freq bits F2-F9
LD A,(IX+7) ;A=frequency step size
CALL __ADD816 ;*Fn95: ADD A TO WORD AT HL
;add the frequency step, also incrementing
;the low freq bits F0-F1 in byte IX+4
INC HL ;point to IX+4: freq bits in low nibble
RES 2,(HL) ;clear bit 2 of frequency
OR 255 ;A=255, ZF=0
RET
;***************************************************************************
;UPDATE VOLUME-SWEPT NOTE subroutine.
; On entry, IX=output table address. On exit, ZF=1 if the note is over,
; ZF=0 if not.
A58103:
LD A,(IX+8) ;A=vol step size/vol step # byte
OR A ;is it zero? (unused)
RET Z ;YES, so exit
PUSH IX ;save output table address...
POP HL ;and get it back in HL
LD DE,9 ;offset
ADD HL,DE ;point HL at byte IX+9: vol step period
;(4 bits), 1st vol step period (4 bits)
CALL __DECLSN ;*Fn92: DECREMENT LOW NIBBLE OF (HL)
;1st vol step period is decremented
RET NZ ;not time to step yet, so exit
CALL __MSNTOLSN ;*Fn94: HIGH NIBBLE OF (HL) TO LOW NIBBLE
;time to step! so move vol step period
;down to low nibble (still in upper)
;all later steps will have this duration
DEC HL ;back up to IX+8: vol step size/step #
CALL __DECLSN ;*Fn92: DECREMENT LOW NIBBLE OF (HL)
;one less step; is it zero?
JR Z,A58149 ;YES, so zero out the byte and exit
LD A,(HL) ;NO, so get it back in A (size old, # new)
AND 240 ;mask out lower 4 bits (leaves size)
LD E,A ;save in in A
DEC HL
DEC HL
DEC HL ;back up 4 to byte IX+4: volume (4 bits),
DEC HL ;low bits of frequency (4 bits)
LD A,(HL) ;get it in A
AND 240 ;mask out lower 4 bits (leaves volume)
ADD A,E ;add step size to volume
LD E,A ;save it in E
LD A,(HL) ;get back vol/freq byte
AND 15 ;mask out upper 4 bits (leaves freq)
OR E ;merge in new volume as upper 4 bits
LD (HL),A ;save it back in output table
OR 255 ;A=255, ZF=0
RET
;***************************************************************************
A58149:
LD (HL),0 ;zero out vol step size/step # byte
RET
;***************************************************************************
;SEND NOTE VOLUME subroutine.
; On entry, IX=output table address, C=voice-dependent volume base as in
; the following table:
; 144 [1001 0000] -- voice 1
; 176 [1011 0000] -- voice 2
; 208 [1101 0000] -- voice 3
; 240 [1111 0000] -- noise volume
; 224 [1110 0000] -- periodic noise
; 228 [1110 0100] -- white noise
A58152:
LD A,(IX+4) ;A=volume/frequency byte
BIT 4,C ;is bit 4 set? (voice 1,2,3, noise vol)
JR Z,A58163 ;NO, so skip the shifting
RRCA ;YES, so get volume
RRCA
RRCA
RRCA ;volume in lower 4 bits, freq in upper 4
A58163:
AND 15 ;wipe out upper 4 bits
OR C ;add voice-dependent base
JP A58893 ;SEND DATA (in A) OUT SOUND PORT subrt
;***************************************************************************
;SEND NOTE FREQUENCY subroutine.
; On entry, IX=output table address, D=voice-dependent frequency base as in
; the following table:
; 128 [1000 0000] -- voice 1
; 160 [1010 0000] -- voice 2
; 192 [1100 0000] -- voice 3
A58169:
LD A,(IX+3) ;A=frequency byte (high 8 bits)
AND 15 ;mask out upper 4 bits
OR D ;merge with voice-dependent frequency base
CALL A58893 ;SEND DATA (in A) OUT SOUND PORT subrt
LD A,(IX+3) ;get back frequency byte
AND 240 ;this time, mask out lower 4 bits
LD D,A ;save it in D
LD A,(IX+4) ;A=vol/freq byte (low nibble is freq)
AND 15 ;mask out upper 4 bits
OR D ;merge with other frequency bits
RRCA
RRCA
RRCA
RRCA ;effectively, swap nibbles
JP A58893 ;SEND DATA (in A) OUT SOUND PORT subrt
;***************************************************************************
;EOS Function 92: DECREMENT LOW NIBBLE OF (HL).
; On exit, the low nibble of (HL) is decremented, A=new value of low
; nibble, and ZF=1 if it is now zero.
__DECLSN:
XOR A ;A=0
RRD ;move low nibble of (HL) into A
SUB 1 ;decrement it
PUSH AF ;save it with flags
RLD ;move decremented nibble back to (HL)
POP AF ;restore nibble value in A with flags
RET
;***************************************************************************
;EOS Function 93: DECREMENT HIGH NIBBLE OF (HL).
; On exit, the high nibble of (HL) is decremented, A=new value of high
; nibble, and ZF=1 if it is now zero. This function is not used anywhere
; in EOS-5.
__DECMSN:
XOR A ;A=0
RLD ;move high nibble of (HL) into A
SUB 1 ;decrement it
PUSH AF ;save it with flags
RRD ;move decremented nibble back to (HL)
POP AF ;restore nibble value in A with flags
RET
;***************************************************************************
;EOS Function 94: HIGH NIBBLE OF (HL) TO LOW NIBBLE.
; On exit, the high nibble of (HL) is moved to the low nibble, with the
; original high nibble unchanged.
__MSNTOLSN:
LD A,(HL) ;A=memory byte
AND 240 ;mask out low nibble
LD B,A ;save it in B
RRCA
RRCA
RRCA
RRCA ;shift the high nibble down to low
OR B ;merge in the original high nibble
LD (HL),A ;put it back in memory
RET
;***************************************************************************
;EOS Function 95: ADD A TO WORD AT HL.
; On entry, A=data to add, (HL)=lobyte of word, (HL+1)=hibyte of word. On
; exit, A is added to word, and HL still points to the lobyte.
__ADD816:
LD B,0 ;B=0
BIT 7,A ;is bit 7 set? (>=128)
JR Z,A58235 ;NO, so no carry to hibyte
DEC B ;YES, so maybe a carry; B=255
A58235:
ADD A,(HL) ;add A to lobyte of word
LD (HL),A ;save lobyte sum
INC HL ;point to hibyte
LD A,(HL) ;get it in A
ADC A,B ;add B to hibyte for carry
LD (HL),A ;save hibyte sum
DEC HL ;point back to lobyte
RET
;***************************************************************************
;GET ADDRESS OF OUTPUT TABLE (in IX) subroutine.
; On entry, B=code for voice (1=noise, 2=voice1, 3=voice2, 4=voice3). On
; exit, IX=address of output table for the requested voice, HL points to
; hibyte of output table address in voice table.
A58243:
LD HL,(PTR_TO_LST_OF_SND_ADDRS) ;HL=address of voice list
DEC HL
DEC HL ;back up 2 (gives required offset)
LD C,B
LD B,0 ;BC=B
RLC C ;BC*2
RLC C ;BC*4
ADD HL,BC ;offset into voice table
LD E,(HL) ;E=lobyte of output table address
INC HL ;point to hibyte
LD D,(HL) ;D=hibyte of output table address
PUSH DE ;save it on stack
POP IX ;get it back in IX
RET
;***************************************************************************
;GET SPECIAL EFFECTS NOTE EXECUTION ADDRESS (in HL) subroutine.
; On entry, IX=output table address. On exit, if the current note is
; special effects, HL=execution address, and A=62. If there is no note,
; A=255. A is unpredictable if the note is anything other than special
; effects.
A58263:
LD A,(IX+0) ;A=current note from output table
CP 255 ;is it 255? (no note)
RET Z ;YES, so exit
AND 63 ;mask out upper 2 bits
CP 62 ;is it 62? (special effects)
RET NZ ;NO, so exit
PUSH IX ;YES, so save output table address...
POP HL ;and get it back in HL
INC HL ;point to lobyte of execution address
LD E,(HL) ;get it in E
INC HL ;point to hibyte
LD D,(HL) ;get it in D
EX DE,HL ;swap execution address into HL
RET
;***************************************************************************
;EOS Function 96: SOUND INITIALIZATION.
; On entry, B=number of voices to initialize (1-4), HL=address of song
; table with the following format:
; address of noise note table (lobyte, hibyte)
; address of noise output table (lobyte, hibyte)
; ...same for voices 1,2,3.
; On exit, the current note of each voice in the output table and the saved
; control sound are set to null (255), and the EOS voice table pointers are
; set to 58342 (address of another null).
__SOUND_INIT:
LD (PTR_TO_LST_OF_SND_ADDRS),HL ;save voice table address in RAM
INC HL
INC HL ;point ahead 2 to output table address
LD E,(HL) ;get lobyte of address
INC HL ;point to hibyte
LD D,(HL) ;get hibyte of address
EX DE,HL ;save output table address in HL
LD DE,10 ;length of note entry in table=10 bytes
LD A,255 ;code for no note being played=255
A58297:
LD (HL),A ;save it as first byte of note entry
ADD HL,DE ;point to next note in output table
DJNZ A58297 ;keep initializing 'til all voices done
LD (HL),0 ;end with a zero to show no more voices
LD HL,A58342 ;HL=address of null sound (58342)=255
LD (PTR_TO_S_ON_0),HL ;noise pointer
LD (PTR_TO_S_ON_1),HL ;voice 1 pointer
LD (PTR_TO_S_ON_2),HL ;voice 2 pointer
LD (PTR_TO_S_ON_3),HL ;voice 3 pointer
LD (SAVE_CTRL),A ;saved control sound=255
;falls through to Fn97: SOUND OFF
;***************************************************************************
;EOS Function 97: SOUND OFF.
; On exit, the three voices and the noise channel are turned off.
__TURN_OFF_SOUND:
LD A,(SOUNDPORT) ;A=sound port
LD C,A ;save it in C
LD A,159 ;volume off voice 1
OUT (C),A ;send it
LD A,191 ;volume off voice 2
OUT (C),A ;send it
LD A,223 ;volume off voice 3
OUT (C),A ;send it
LD A,255 ;volume off noise channel
OUT (C),A ;send it
RET
;***************************************************************************
;NULL SOUND FOR EOS VOICE TABLE INITIALIZATION.
A58342:
DB 255
;***************************************************************************
;EOS Function 98: START VOICE.
; On entry, B=number of the voice to start (1=noise, 2=voice1, 3=voice2,
; 4=voice3. On exit, the first note of that voice is ready to play.
__PLAY_IT:
PUSH BC ;save voice number
CALL A58243 ;GET ADDR OF OUTPUT TABLE (in IX) subrt
LD A,(IX+0) ;A=current note from output table
AND 63 ;mask out voice bits (leaving song #)
;7-6=voice; 5-0=song #
POP BC ;restore voice number
CP B ;is it already started? (same)
RET Z ;YES, so exit
LD (IX+0),B ;NO, so put voice # in output table
DEC HL ;back up 2 in song table to get note
DEC HL ;table addr (HL set by subroutine)
LD D,(HL)
DEC HL
LD E,(HL) ;DE=note table address
LD (IX+1),E
LD (IX+2),D ;put it in the output table
CALL A58610 ;NOTE, REST, REPEAT OR END subroutine
JR A58490 ;LOAD POINTERS TO VOICE OUTPUT TABLES sub
;RET is here
;***************************************************************************
;EOS Function 99: SOUND.
; On entry, all necessary voice tables must have been properly set up and
; initialized.
__SOUNDS:
LD A,159 ;off volume voice 1
LD C,144 ;volume base voice 1
LD D,128 ;frequency base voice 1
LD IX,(PTR_TO_S_ON_1) ;IX=address of voice 1 output table
CALL A58454 ;PLAY A NOTE subroutine
LD A,191 ;off volume voice 2
LD C,176 ;volume base voice 2
LD D,160 ;frequency base voice 2
LD IX,(PTR_TO_S_ON_2) ;IX=address of voice 2 output table
CALL A58454 ;PLAY A NOTE subroutine
LD A,223 ;off volume voice 3
LD C,208 ;volume base voice 3
LD D,192 ;frequency base voice 3
LD IX,(PTR_TO_S_ON_3) ;IX=address of voice 3 output table
CALL A58454 ;PLAY A NOTE subroutine
LD A,255 ;off volume noise channel
LD C,240 ;volume base noise channel
LD IX,(PTR_TO_S_ON_0) ;IX=address of noise output table
LD E,(IX+0) ;get current note in E
INC E ;is it 255? (no note)
JR NZ,A58432 ;NO, it's OK
CALL A58893 ;YES, so SEND DATA (in A) OUT SOUND PORT
JR A58469 ;CHECK IF EACH VOICE IS DONE subroutine
;***************************************************************************
A58432:
CALL A58152 ;SEND NOTE VOLUME subroutine
LD A,(IX+4) ;A=note vol/freq byte from output table
AND 15 ;mask out upper 4 bits (leaves 0 0 F0 F1)
LD HL,SAVE_CTRL ;HL=address of saved control sound
CP (HL) ;is it the same?
JR Z,A58469 ;YES, so CHECK IF EACH VOICE IS DONE subrt
LD (HL),A ;NO, so update with current value
LD C,224 ;volume base for periodic nose
CALL A58152 ;SEND NOTE VOLUME subroutine
JR A58469 ;CHECK IF EACH VOICE IS DONE subroutine
;***************************************************************************
;PLAY A NOTE subroutine.
; On entry, IX=address of output table, A=off volume for voice, C=volume
; base for voice, D=frequency base for voice. On exit, if the current
; note=255 (no note), the current voice is turned off. Otherwise, the
; note is played.
A58454:
LD E,(IX+0) ;E=current note from output table
INC E ;is it 255? (no note)
JR NZ,A58463 ;NO, note is OK
JP A58893 ;YES, so SEND DATA (in A) OUT SOUND PORT
;***************************************************************************
A58463:
CALL A58152 ;SEND NOTE VOLUME subroutine
JP A58169 ;SEND NOTE FREQUENCY subroutine
;***************************************************************************
;CHECK IF EACH VOICE IS DONE subroutine.
A58469:
LD B,1 ;code for noise channel
CALL A58243 ;GET ADDR OF OUTPUT TABLE (in IX) subrt
A58474:
LD A,0 ;A=0
CP (IX+0) ;is it the end of the output table?
RET Z ;YES, so exit
CALL A58573 ;NO, so FINISH PLAY AND CHECK IF DONE sub
LD DE,10 ;length of output table entry
ADD IX,DE ;point to next entry
JR A58474 ;keep going 'til end of table
;***************************************************************************
;LOAD POINTERS TO VOICE OUTPUT TABLES subroutine.
A58490:
PUSH IX
LD HL,A58342 ;address of null sound
LD (PTR_TO_S_ON_0),HL ;init noise pointer
LD (PTR_TO_S_ON_1),HL ;init voice 1 pointer
LD (PTR_TO_S_ON_2),HL ;init voice 2 pointer
LD (PTR_TO_S_ON_3),HL ;init voice 3 pointer
LD B,1
CALL A58243 ;GET ADDR OF OUTPUT TABLE (in IX) subrt
A58512:
LD A,(IX+0) ;A=current note
OR A ;is it zero? (end of output table)
JR Z,A58549 ;YES, so exit
INC A ;NO, but is it 255? (no note)
JR Z,A58542 ;YES, so point to next note
LD A,(IX+0) ;NO, this note's OK, so get it back
AND 192 ;select voice bits (7-6)
RLCA
RLCA
RLCA ;effectively, A=voice*2
LD E,A
LD D,0 ;DE=voice offset
LD HL,PTR_TO_S_ON_0 ;base of pointers to voice output tables
ADD HL,DE ;select sound (DE=0,1,2,3)
PUSH IX ;save output table address...
POP DE ;and get it back in DE
LD (HL),E ;lobyte of address to EOS table
INC HL ;point to next slot
LD (HL),D ;hibyte of address to EOS table
A58542:
LD DE,10 ;length of output table entry
ADD IX,DE ;offset to next entry
JR A58512 ;keep going
;***************************************************************************
A58549:
POP IX
RET
;***************************************************************************
;EOS Function 100: END SPECIAL EFFECTS.
; On entry, IX=output table address, HL=address of next special effects
; note, DE=?.
__EFFECT_OVER:
LD (IX+1),L
LD (IX+2),H ;store next note exec addr in output table
LD A,(DE) ;get A from (DE)
AND 63 ;mask out upper 2 bits
LD B,A ;save it in B
LD A,(IX+0) ;get old note
AND 192 ;mask out lower 6 bits
OR B ;merge with B
LD (IX+0),A ;save it back in output table
JR A58595 ;copy next note to output table
;***************************************************************************
;FINISH PLAY AND CHECK IF DONE subroutine.
; If the note is special effects, it is executed. If not, volume and
; frequency sweeps and note lengths are updated. If the note is over, the
; next note from the note table is loaded into the output table. Otherwise,
; nothing happens.
A58573:
CALL A58263 ;GET SPECIAL FX NOTE EXEC ADDRESS (in HL)
CP 255 ;was there a note?
RET Z ;NO, so exit
CP 62 ;YES, but was it special effects?
JR NZ,A58588 ;NO, a regular note
LD DE,7 ;YES, it was special effects
ADD HL,DE ;offset 7 from address
JP (HL) ;play the special effects note
;***************************************************************************
A58588:
CALL A58103 ;UPDATE VOLUME-SWEPT NOTE subroutine
CALL A58055 ;UPDATE SIMPLE NOTE subroutine
RET NZ ;note not over, so exit
A58595:
LD A,(IX+0) ;note is done! get old note
PUSH AF ;save it
CALL A58610 ;NOTE, REST, REPEAT OR END subroutine
POP BC ;restore old note
LD A,(IX+0) ;get new note from output table
CP B ;is it the same voice and kind?
RET Z ;YES, so exit
JR A58490 ;NO, so LOAD POINTERS TO
;VOICE OUTPUT TABLES subroutine
;***************************************************************************
;NOTE, REST, REPEAT OR END subroutine.
; On entry, IX=address of output table. On exit, the output table is set
; up to play the next note.
A58610:
LD A,(IX+0) ;A=current note from output table
AND 63 ;mask out voice bits, leaving song #
PUSH AF ;save song #
LD (IX+0),255 ;initialize current note to null
LD L,(IX+1)
LD H,(IX+2) ;HL=address of next note in note table
LD A,(HL) ;A=next note
LD B,A ;save it in B
BIT 5,A ;is bit 5 set? (REST)
JR Z,A58660 ;NO, it's a NOTE, REPEAT or END
PUSH BC ;YES, so save rest on stack
AND 31 ;mask out lower 5 bits (get rest length)
INC HL ;point to next note in note table
LD (IX+1),L ;(rest note is 1 byte long)
LD (IX+2),H ;save addr of next note in output table
LD (IX+4),240 ;put volume in output table
LD (IX+5),A ;put length in output table
LD (IX+7),0
LD (IX+8),0 ;zero out volume and frequency sweeps
JP A58860 ;EXIT
;***************************************************************************
;REPEAT SONG.
A58660:
BIT 4,A ;is bit 4 set? (END or REPEAT)
JR Z,A58678 ;NO, it's a NOTE
BIT 3,A ;YES, so it bit 3 set? (REPEAT)
JR Z,A58672 ;NO, so END the voice
POP BC ;YES, so restore the voice number
;to REPEAT
JP __PLAY_IT ;*Fn98: START VOICE
;***************************************************************************
;END VOICE.
A58672:
LD A,255 ;A=null value for output table
PUSH AF ;save it
JP A58860 ;EXIT
;***************************************************************************
;SPECIAL EFFECTS NOTE.
; If the note is special effects, the programmer must supply the address of
; a RAM routine to set up the effect. With no examples to study, I can
; only guess at how this works. It seems, however, that this setup routine
; has 2 parts: the first (at the address stored in the special effects
; note) does the setup for playing the current note. This setup must exit
; with a RET instruction, which returns to 58712, with another execution
; address base in IY. (I don't know if the return IY is the same as the
; entry IY.) The exit return address (58860) is saved on the stack, and
; then a branch is made to IY+7. Presumably this second routine does some
; setup for the next special effects note.
A58678:
AND 60 ;mask out bits 7-6, 1-0
CP 4 ;is it 4? (bit 2 set=SPECIAL EFFECTS)
JR NZ,A58723 ;NO, it's something else
POP IY ;YES, IY=song #/old flags (pushed as AF)
PUSH IY ;save it
PUSH BC ;B has 1st byte of note from note table
INC HL ;point HL to 2nd byte
LD E,(HL) ;E=lobyte of special effects execute addr
LD (IX+1),E ;save it in output table
INC HL ;point to 3rd byte of note table
LD D,(HL) ;D=hibyte of special effects execute addr
LD (IX+2),D ;save it in output table
INC HL ;point to start of next note in note table
PUSH IY ;save song #/old flags
POP AF ;restore A=song #, old flags
PUSH DE ;save special effects execution address
POP IY ;get it back in IY
LD DE,A58712 ;return address
PUSH DE ;save it
JP (IY) ;set up the special effects note
;***************************************************************************
A58712:
LD DE,7 ;DE=7
ADD IY,DE ;offset from IY (but what is IY?)
LD DE,A58860 ;return address
PUSH DE ;save it
JP (IY) ;do something else...
;***************************************************************************
;SIMPLE NOTE.
A58723:
PUSH BC ;save current note
LD A,B ;get it in A
AND 3 ;mask out bits 7-2
OR A ;is it zero? (SIMPLE NOTE)
JR NZ,A58762 ;NO, more complex
INC HL ;YES, so point ahead 4 to start of next
INC HL ;note in note table
INC HL ;(simple note is 4 bytes long)
INC HL
LD (IX+1),L
LD (IX+2),H ;save addr of next note in output table
DEC HL ;back up 1
LD DE,5 ;DE=5 (offset into output table)
CALL A58883 ;DE=IX+DE subroutine
LD BC,3 ;number of bytes to copy
LDDR ;copy note table data to output table
LD (IX+7),0
LD (IX+8),0 ;zero out unused parts of output table
JR A58860 ;EXIT
;***************************************************************************
;FREQUENCY-SWEPT NOTE.
A58762:
CP 1 ;is it 1? (frequency-swept note)
JR NZ,A58792 ;NO, it's something else
LD DE,6 ;length of frequency-swept note=6 bytes
ADD HL,DE ;get address of next note from note table
LD (IX+1),L
LD (IX+2),H ;save address in output table
DEC HL ;back up 1 in note table
INC E ;offset=6 into output table
CALL A58883 ;DE=IX+DE subroutine
LD BC,5 ;number of bytes to copy
LDDR ;copy note table data to output table
LD (IX+8),0 ;zero out unused parts of output table
JR A58860 ;EXIT
;***************************************************************************
;VOLUME-SWEPT/NOISE NOTE.
; Volume-swept notes are voices 1,2,3; noise notes are voice 0.
A58792:
CP 2 ;is it 2? (VOLUME-SWEPT/NOISE)
JR NZ,A58834 ;NO, it's something else
LD DE,6 ;length of note=6 bytes (5 for NOISE)
ADD HL,DE ;point to start of next note in note table
POP AF ;restore A=byte 1 of current note saved
;way back when (58632) as BC
PUSH AF ;save it again
AND 192 ;mask out bits 5-0; is it noise? (voice 0)
JR NZ,A58807 ;NO, it's VOLUME-SWEPT
DEC HL ;YES, it's NOISE, so back up 1
A58807:
LD (IX+1),L
LD (IX+2),H ;save address of next note in note table
DEC HL ;back up 1
LD E,9 ;offset into output table=9
CALL A58883 ;DE=IX+DE subroutine
LD BC,2 ;number of bytes to copy
LDDR ;copy vol step bytes to IX+8, IX+9
XOR A ;A=0
LD (DE),A ;zero out IX+7 (freq step size byte)
DEC DE ;back up 2
DEC DE ;(freq length, freq step length unchanged)
LD C,3 ;number of bytes to copy
LDDR ;copy note length, volume, frequency
JR A58860 ;EXIT
;***************************************************************************
;VOLUME- AND FREQUENCY-SWEPT NOTE.
A58834:
LD DE,8 ;length of vol+freq swept note=8 bytes
ADD HL,DE ;point to start of next note in note table
LD (IX+1),L
LD (IX+2),H ;save address of next note in output table
DEC HL ;back up 1
PUSH IX ;save base of output table...
POP IY ;and get it back in IY
INC E ;DE=9
ADD IY,DE ;offset into output table
PUSH IY ;save it...
POP DE ;and get it back in DE
LD BC,7 ;number of bytes to move
LDDR ;copy note table data to output table
;***************************************************************************
;SOUND SETUP EXIT.
A58860:
PUSH IX ;save address of output table...
POP HL ;and get it back in HL
POP AF ;restore 1st byte of note from note table
POP BC ;restore 1st byte of current note (output)
CP 255 ;is it null?
RET Z ;YES, so exit
LD D,A ;NO, so D=1st byte of note from note table
AND 63 ;mask out voice bits, leaving note type
CP 4 ;is it 4? (special effects)
JR NZ,A58877 ;NO
LD B,62 ;YES, so B=62
A58877:
LD A,D ;A=1st byte of note from note table
AND 192 ;get the voice bits (mask out 5-0)
OR B ;set the voice bits
LD (HL),A ;save voice/song at byte 0 in output table
RET
;***************************************************************************
;DE=IX+DE subroutine.
; On exit, DEout=IYin+DEin.
A58883:
PUSH IX
POP IY ;IY=IX
ADD IY,DE ;IY=IY+DE
PUSH IY
POP DE ;DE=IY
RET
;***************************************************************************
;SEND DATA (in A) OUT SOUND PORT subroutine.
; On entry and exit, A=data to send.
A58893:
PUSH BC
LD B,A ;B=data to send
LD A,(SOUNDPORT) ;get sound port...
LD C,A ;in C
OUT (C),B ;send the data
LD A,B ;restore A with data
POP BC
RET
;***************************************************************************
;EOS Function 69: FIND FILE (NO TYPE).
; On entry, A=device number, DE=address of filename string (10 characters
; max for name, then filetype byte , then hex 03), HL=address
; of 23-byte buffer to contain the directory entry (no date bytes). The
; routine reads the directory and looks for the first match to the file
; name string. The 10-character filenames must match, but the filetype
; bytes need not. On exit, if a match was found, the buffer at HL contains
; the directory entry, BCDE=start block of file, and ZF=1. Otherwise,
; ZF=0 and A=error code.
__FILE_QUERY:
SCF ;set carry flag
JR A58909 ;jump into next routine
;***************************************************************************
;EOS Function 52: FIND FILE (WITH TYPE).
; On entry, A=device number, DE=address of filename string (10 characters
; max for name, then filetype byte , then hex 03), HL=address
; of 23-byte buffer to contain the directory entry (no date bytes). The
; routine reads the directory and looks for the first match to the file
; name string. Unlike Fn69, both the 10-character filenames and the file
; type bytes must match. On exit, if a match was found, the buffer at HL
; contains the directory entry, BCDE=start block of file, and ZF=1.
; Otherwise, ZF=0 and A=error code.
__QUERY_FILE:
SCF
CCF ;effectively, clear carry flag
A58909:
PUSH HL
PUSH IX
PUSH AF ;save device #
JR C,A58916 ;if Fn69 (find file, no type) A is nonzero
XOR A ;if Fn52 (find file, with type) A=0
A58916:
LD (FILENAME_CMPS),A ;save A as filename comparison
;zero=type must match, anything else=name only
POP AF ;restore A=device #
LD (USER_BUF),HL ;save address of directory entry buffer
LD IX,(FCB_HEAD_ADDR) ;IX=address of FCB0
LD H,D
LD L,E ;HL=address of filename string
CALL __SCAN_FOR_FILE ;*Fn68: READ DIRECTORY FOR FILE
JR NZ,A58957 ;file not found, so error exit ZF=0
PUSH DE ;file found! FCB0 bytes 33-34 have offset
PUSH BC ;address of matching entry in DTA0
LD DE,(USER_BUF) ;DE=directory entry buffer address
LD L,(IX+33)
LD H,(IX+34) ;HL=address of directory entry in DTA0
LD BC,23 ;23 bytes to move
LDIR ;copy directory entry into user buffer
POP BC
POP DE ;BCDE=start block of file
XOR A ;A=0, ZF=1
LD (FILENAME_CMPS),A ;file name comparison=0 (type must match)
A58957:
POP IX
POP HL
RET
;***************************************************************************
;EOS Function 53: UPDATE DIRECTORY ENTRY.
; On entry, HL=address of 23-byte buffer containing a directory entry (no
; date bytes), DE=address of filename string, and FCB0 is set up to read
; the directory block(s). On exit, if the file already exists, the entry
; is updated, A=0 and ZF=1. Otherwise, ZF=0 and A=error code.
__SET_FILE:
PUSH BC
PUSH DE
PUSH HL
PUSH IX
LD (USER_BUF),HL ;save address of buffer with new entry
LD IX,(FCB_HEAD_ADDR) ;IX=address of FCB0
LD H,D
LD L,E ;HL=address of filename string
CALL __SCAN_FOR_FILE ;*Fn68: READ DIRECTORY FOR FILE
JR NZ,A59018 ;file not found, so error exit ZF=0
LD HL,(USER_BUF) ;found! so restore HL=entry buffer address
LD E,(IX+33) ;Fn68 returns DTA offset addr of match
LD D,(IX+34) ;in FCB0 bytes 33-34, so get this in DE
LD BC,23 ;length of entry
LDIR ;copy updated entry to DTA0
LD A,(IX+23) ;A=device #
LD HL,(FCB_DATA_ADDR) ;HL=address of DTA0
LD E,(IX+25)
LD D,(IX+26)
LD C,(IX+27)
LD B,(IX+28) ;BCDE=block number to write
CALL __WRITE_BLOCK ;*Fn66: WRITE BLOCK
JR NZ,A59018 ;write failed, so error exit ZF=0
XOR A ;write OK, so exit A=0, ZF=1
A59018:
POP IX
POP HL
POP DE
POP BC
RET
;***************************************************************************
;EOS Function 51: CREATE FILE.
; On entry, A=device number, BCDE=length of file in bytes (BC=hiword, DE=
; loword), HL=address of filename string. If BCDE=0, the file will not
; attempt to reuse deleted file space, thus allowing for maximum file size.
; On exit, if create was successful, an entry for the file is added to the
; directory, "BLOCKS LEFT" is updated, ZF=1 and A=0. Otherwise, ZF=0 and
; A=error code.
__MAKE_FILE:
PUSH IY
PUSH IX
PUSH HL
PUSH DE
PUSH BC
LD IX,(FCB_HEAD_ADDR) ;IX=address of FCB0
LD (IX+24),0 ;zero out I/O mode byte
LD (IX+23),A ;save device # in FCB0
LD (USER_NAME),HL ;save address of filename string
LD A,B
OR C ;is BC=zero?
JR NZ,A59059 ;NO, so check if file too big
LD A,E ;YES, so check DE
OR D ;is DE=zero?
JR NZ,A59059 ;NO, so check if file too big
SET 5,(IX+24) ;YES, BCDE=zero, set bit 5 I/O mode byte
;(don't resuse deleted files)
JR A59091 ;skip size check
;***************************************************************************
;FILE SIZE CHECK.
; Maximum file size is 254 blocks (leaves 1 for boot and 1 for directory).
A59059:
LD E,D
LD D,C
LD C,B
LD B,0 ;effectively, BCDE/256
SRL C
RR D
RR E ;BCDE/512
SRL C
RR D
RR E ;BCDE/1024 (converts bytes to blocks)
LD A,C
OR A ;is C=0? (blocks less than 255)
JP NZ,A59794 ;NO, so exit error 11 (FILE TOO BIG)
INC DE ;add 1 to DE (if DE was 255, now it's 0)
LD A,E
OR D ;is DE=0? (did we want a 255-block file)
JP Z,A59794 ;YES, so exit error 11 (FILE TOO BIG)
LD (BLOCKS_REQ),DE ;save loword of file length in blocks
;***************************************************************************
A59091:
LD DE,0
LD (BLOCKS_REQ+2),DE ;zero out hiword of file length in blocks
LD (DIR_BLOCK_NO),DE ;directory block number=0
XOR A
LD (FOUND_AVAIL_ENT),A ;zero out found entry byte
LD (IX+25),1
LD (IX+26),A
LD (IX+27),A
LD (IX+28),A ;block to read=1 (directory)
LD (IX+13),1
LD (IX+14),A
LD (IX+15),A
LD (IX+16),A ;start block=1
LD DE,(FCB_DATA_ADDR) ;DE=address of DTA0
LD (IX+33),E
LD (IX+34),D ;save address of DTA0 in FCB0
LD (BUF_START),DE ;DTA0 is also buffer start
LD HL,1024 ;length of buffer=1024
ADD HL,DE ;point to end of buffer
LD (BUF_END),HL ;save buffer end address
LD A,(IX+23) ;A=device #
LD L,(IX+33)
LD H,(IX+34) ;HL=transfer address
LD E,(IX+25)
LD D,(IX+26)
LD C,(IX+27)
LD B,(IX+28) ;BCDE=block to read
CALL __READ_BLOCK ;*Fn65: READ BLOCK
JP NZ,A59785 ;read failed, so error exit ZF=0
LD IY,(FCB_DATA_ADDR) ;read OK, so IY=address of DTA0
LD A,(IY+12) ;A=directory size byte from volume entry
AND 127 ;mask out bit 7
LD (IX+29),A ;save max length of directory in FCB0
LD (IX+30),0
LD (IX+31),0
LD (IX+32),0
CALL A61493 ;VERIFY DIRECTORY CHECK CODE subroutine
JP NZ,A59785 ;check failed, so error exit ZF=0
LD B,38 ;check OK, so 38 more entries to check
A59212:
LD L,(IX+33)
LD H,(IX+34) ;HL=address of DTA0 (1st dir entry)
LD DE,26 ;length of dir entry
ADD HL,DE ;point to next entry
LD (IX+33),L
LD (IX+34),H ;save this address in FCB0
A59228:
LD L,(IX+33)
LD H,(IX+34) ;HL=entry address from FCB0
PUSH HL ;save it...
POP IY ;and get it back in IY
LD A,(IY+12) ;A=attribute byte of file
LD C,A ;save in in C
BIT 0,A ;is bit 0 set? (NOT A FILE)
JP NZ,A59399 ;YES, so we're at BLOCKS LEFT (last entry)
;see if we found a free entry
BIT 5,(IX+24) ;NO, but is bit 5 of I/O mode byte set?
;(don't reuse deleted files)
JR NZ,A59307 ;YES, so keep looking
BIT 2,C ;NO, but is bit 2 of file attribute set?
;(file deleted)
JR Z,A59292 ;NO, file exists, so check if it has the
;same name as the one we want to create
;***************************************************************************
;DELETED FILE SIZE CHECK.
LD L,(IY+17) ;YES, file is deleted, see how big it is
LD H,(IY+18) ;HL=file length from entry (blocks free)
LD DE,(BLOCKS_REQ) ;DE=file length in blocks (blocks needed)
OR A ;clear CF
SBC HL,DE ;is free less than needed?
JR C,A59307 ;YES, not enough blocks, so keep looking
LD HL,FOUND_AVAIL_ENT ;NO, enough room; HL=found entry byte addr
BIT 0,(HL) ;is bit 0 set? (FOUND A FREE ENTRY)
JR NZ,A59307 ;YES, so keep looking
SET 0,(HL) ;NO, so set the found entry byte
LD E,(IX+25)
LD D,(IX+26) ;DE=loword of last block read
LD (DIR_BLOCK_NO),DE ;save this directory block number
JR A59307 ;keep looking
;***************************************************************************
;FILENAME CHECK.
A59292:
LD E,(IX+33)
LD D,(IX+34) ;DE=offset into DTA0 of dir entry (name)
LD HL,(USER_NAME) ;HL=address of filename string to compare
CALL A61524 ;FILENAME COMPARISON WITHOUT TYPE subrt
JP Z,A59798 ;file already exists, so error 6 exit
A59307:
DJNZ A59212 ;keep looking 'til all checked or end
;***************************************************************************
;MULTIPLE DIRECTORY BLOCK HANDLER.
LD HL,(BUF_START) ;all the way through without finding
;BLOCKS LEFT, so there must be more
;directory blocks. HL=buffer start addr
LD (IX+33),L
LD (IX+34),H ;save buffer start address in FCB0
INC (IX+25) ;point to next directory block
LD A,(IX+32) ;get hiword/hibyte of dir size
CP (IX+28) ;is it less than last block read?
JP C,A59389 ;YES, so no more directory
LD A,(IX+31) ;NO, so get hiword/lobyte of dir size
CP (IX+27) ;is it less than last block read?
JP C,A59389 ;YES, so no more directory
LD A,(IX+30) ;NO, so get loword/hibyte of dir size
CP (IX+26) ;is it less than last block read?
JP C,A59389 ;YES, so no more directory
LD A,(IX+29) ;NO, so get loword/lobyte of dir size
CP (IX+25) ;is it less than last block read?
JP C,A59389 ;YES, so no more directory
LD A,(IX+23) ;NO, still dir blocks left, so A=device #
LD L,(IX+33)
LD H,(IX+34) ;HL=address of DTA0 from FCB0
LD E,(IX+25)
LD D,(IX+26)
LD C,(IX+27)
LD B,(IX+28) ;BCDE=block to read
CALL __READ_BLOCK ;*Fn65: READ BLOCK
JP NZ,A59785 ;read failed, so error exit ZF=0
LD B,39 ;read OK...
JP A59228 ;so read 39 more files
;***************************************************************************
;NO MORE DIRECTORY BLOCKS TO READ...DID WE FIND ANYTHING?
A59389:
LD HL,FOUND_AVAIL_ENT ;HL=address of found entry byte
BIT 0,(HL) ;is bit 0 set? (FOUND A FREE ENTRY)
JP Z,A59802 ;NO, we read the whole thing without luck
;exit error 12
JR A59406 ;YES, we found something
;***************************************************************************
;"BLOCKS LEFT" REACHED...DID WE FIND ANYTHING?
A59399:
LD HL,FOUND_AVAIL_ENT ;HL=address of found entry byte
BIT 0,(HL) ;is bit 0 set? (FOUND A FREE ENTRY)
JR Z,A59506 ;NO, but see if there's room at the end
;to make a new one
A59406:
RES 0,(HL) ;YES, so clear the found entry byte
LD L,(IX+25)
LD H,(IX+26) ;HL=next block number
LD DE,(DIR_BLOCK_NO) ;DE=last directory block number read
OR A ;clear CF
SBC HL,DE ;HL=HL-DE; is next the same as last?
LD HL,(BUF_START) ;HL=address of buffer start
LD (IX+33),L
LD (IX+34),H ;set buffer start address in FCB0
JR Z,A59468 ;next block same as last, so skip reading
LD (IX+25),E ;next block not same as last, so read next
LD (IX+26),D ;save new current block in FCB0
LD BC,0 ;zero out BC
LD A,(IX+23) ;A=device #
CALL __READ_BLOCK ;*Fn65: READ BLOCK
JP NZ,A59785 ;read failed; error exit ZF=0
JR A59468 ;read OK, so get new entry slot
;***************************************************************************
;FIND THE FREE ENTRY (AGAIN) AND UPDATE IT.
A59452:
LD L,(IX+33)
LD H,(IX+34) ;HL=old DTA0 offset address
LD DE,26 ;length of directory entry
ADD HL,DE ;point to next entry
LD (IX+33),L
LD (IX+34),H ;save address in FCB0
A59468:
PUSH HL ;save DTA0 offset address...
POP IY ;and get it back in IY
BIT 2,(IY+12) ;is bit 2 of attribute byte set? (DELETED)
JR Z,A59452 ;NO, so keep looking
LD L,(IY+17) ;YES, so see how big it is
LD H,(IY+18) ;HL=length of deleted file in blocks
LD DE,(BLOCKS_REQ) ;DE=requested file length in blocks
PUSH HL
OR A ;clear CF
SBC HL,DE ;is free length less than requested?
POP HL
JR C,A59452 ;YES, not enough room, so keep looking
LD (BLOCKS_REQ),HL ;NO, so enough room; save requested length
CALL A59839 ;SET UP NEW DIRECTORY ENTRY subroutine
JP NZ,A59785 ;bad filename error 14, so exit
JP A59761 ;setup OK, so write the new block
;***************************************************************************
;MOVE "BLOCKS LEFT" TO MAKE ROOM FOR NEW ENTRY.
A59506:
LD A,(IX+28) ;get hiword/hibyte of last block read
CP (IX+32) ;is it less than max dir block?
JR C,A59544 ;YES, so there's room to add an entry
LD A,(IX+27) ;NO, get hiword/lobyte of last block read
CP (IX+31) ;is it less than max dir block?
JR C,A59544 ;YES, so there's room
LD A,(IX+26) ;NO, get loword/hibyte of last block read
CP (IX+30) ;is it less than max dir block?
JR C,A59544 ;YES, so there's room
LD A,(IX+25) ;NO, get loword/lobyte of last block read
CP (IX+29) ;is it less than max dir block?
JR C,A59544 ;YES, so there's room
LD A,B ;NO, we're at the last directory block
CP 1 ;but is BLOCKS LEFT in slot #39? (last)
;(B was counter from 39 to 0)
JP Z,A59806 ;YES, so exit error 13 (NO MORE ROOM)
A59544:
LD L,(IY+17)
LD H,(IY+18) ;NO, so HL=length of free space in blocks
BIT 5,(IX+24) ;is bit 5 of I/O mode byte set?
;(don't reuse deleted files)
JR Z,A59568 ;NO, so skip ahead
LD A,H ;YES, so there's got to be free space at
OR L ;the end; is there any?
JP Z,A59806 ;NO, so exit error 13 (NO MORE ROOM)
LD (BLOCKS_REQ),HL ;YES, so save file length in blocks=HL
RES 5,(IX+24) ;clear bit 5 of I/O mode byte
;(no need for this toggle anymore)
A59568:
LD DE,(BLOCKS_REQ) ;DE=requested file length in blocks
OR A ;clear CF
SBC HL,DE ;HL=HL-DE; is free space less than needed?
JP C,A59806 ;YES, so exit error 13 (NO MORE ROOM)
LD (NEW_HOLE_SIZE),HL ;NO, there's room, so save difference
;between free and needed as new hole size
LD HL,BLOCKS_REQ ;point HL to requested file length
LD A,(IY+13) ;A=loword/lobyte of file start block
ADD A,(HL) ;add requested file length to get new hole
;start block
INC HL ;point to next file start block byte
LD (NEW_HOLE_START),A ;save new hole start block loword/lobyte
LD A,(IY+14) ;get file start block loword/hibyte
ADC A,(HL) ;continue addition
INC HL ;point to next file start block byte
LD (65046+1),A ;save new hole start block loword/hibyte
LD A,(IY+15) ;get file start block hiword/lobyte
ADC A,(HL) ;continue addition
INC HL ;point to next file start block byte
LD (65046+2),A ;save new hole start block hiword/lobyte
LD A,(IY+16) ;get file start block hiword/hibyte
ADC A,(HL) ;continue addition
INC HL ;HL=65040 addr of file name str$ pointer
;(this instruction is unnecessary)
LD (65046+3),A ;save new hole start block hiword/hibyte
PUSH BC ;save dir entry counter
CALL A59839 ;SET UP NEW DIRECTORY ENTRY subroutine
POP BC ;restore dir entry counter
JP NZ,A59785 ;bad filename error, so exit
LD L,(IX+33)
LD H,(IX+34) ;filename OK, so HL=address of DTA0
LD DE,26 ;length of dir entry
ADD HL,DE ;point to next entry
LD (IX+33),L
LD (IX+34),H ;store new entry address
DJNZ A59710 ;decrement counter and...
;if "BLOCKS LEFT" was not the last entry
;in the block, then B>0 so put new "BLOCKS
;LEFT" in next dir slot. If is was the
;last entry, B=0 and we must write the
;current block, then read in the next
;block to put "BLOCKS LEFT" on it
;***************************************************************************
;NEW ENTRY AND "BLOCKS LEFT" ON DIFFERENT BLOCKS.
LD A,(IX+23) ;A=device #
LD HL,(BUF_START) ;HL=address of buffer start
LD E,(IX+25)
LD D,(IX+26)
LD C,(IX+27)
LD B,(IX+28) ;BCDE=block number to write
CALL __WRITE_BLOCK ;*Fn66: WRITE BLOCK
JP NZ,A59785 ;write failed, so exit error ZF=0
LD HL,(BUF_START) ;HL=address of buffer start
LD (IX+33),L
LD (IX+34),H ;store buffer start address in FCB0
INC (IX+25) ;point to next directory block
LD A,(IX+23) ;A=device #
LD L,(IX+33)
LD H,(IX+34) ;HL=transfer address (DTA0)
LD E,(IX+25)
LD D,(IX+26)
LD C,(IX+27)
LD B,(IX+28) ;BCDE=block number to read
CALL __READ_BLOCK ;*Fn65: READ BLOCK
JR NZ,A59785 ;read failed, so error exit ZF=0
LD L,(IX+33)
LD H,(IX+34) ;HL=start of DTA0 (first dir entry)
;***************************************************************************
;UPDATE "BLOCKS LEFT" ENTRY.
A59710:
PUSH HL ;save address of dir entry...
POP IY ;and get it back in IY
LD HL,(NEW_HOLE_SIZE) ;HL=new hole size in blocks
LD (IY+17),L
LD (IY+18),H ;save it in dir entry (blocks used)
LD A,(NEW_HOLE_START) ;A=new hole start block loword/lobyte
LD (IY+13),A ;save it in dir entry (start block)
LD A,(65046+1) ;A=new hole start block loword/hibyte
LD (IY+14),A ;save it in dir entry
LD A,(65046+2) ;A=new hole start block hiword/lobyte
LD (IY+15),A ;save it in dir entry
LD A,(65046+3) ;A=new hole start block hiword/hibyte
LD (IY+16),A ;save it in dir entry
LD (IY+12),1 ;set file attribute to 1 (NOT A FILE)
PUSH IY ;save dir entry address...
POP DE ;and get it back in DE
LD HL,A62504 ;start transfer address=BLOCKS LEFT data
;from EOS RAM table
LD BC,12 ;length of name
LDIR ;move BLOCKS LEFT entry to DTA0
A59761:
LD A,(IX+23) ;A=device #
LD HL,(BUF_START) ;HL=buffer start address
LD E,(IX+25)
LD D,(IX+26)
LD C,(IX+27)
LD B,(IX+28) ;BCDE=block number to write
CALL __WRITE_BLOCK ;*Fn66: WRITE BLOCK
JR NZ,A59785 ;write failed, so exit error ZF=1
XOR A ;create file successful, so A=0, ZF=1
A59785:
OR A ;update ZF for OK or error
POP BC
POP DE
POP HL
POP IX
POP IY
RET
;***************************************************************************
A59794:
LD A,11 ;FILE TOO BIG error
JR A59785
;***************************************************************************
A59798:
LD A,6 ;FILE ALREADY EXISTS error
JR A59785
;***************************************************************************
A59802:
LD A,12 ;DIRECTORY FULL error
JR A59785
;***************************************************************************
A59806:
LD A,13 ;NO MORE ROOM error
JR A59785
;***************************************************************************
;CHECK FILENAME LENGTH subroutine.
; On entry, HL=address of filename string, terminated with hex 03. On
; exit, if the string is at least 1 and less than 12 characters long, then
; BC=length of string, A=0 and ZF=1. Otherwise, ZF=0 and A=14 (BAD FILE
; NAME error).
A59810:
PUSH HL
LD B,12 ;max length of filename string=12
LD C,1 ;initialize character counter
A59815:
LD A,(HL) ;get a character
CP 3 ;is it hex 03? (logical end)
JR Z,A59829 ;YES, but were there other characters?
INC C ;NO, so increment counter
INC HL ;point to next character
DJNZ A59815 ;keep going 'til 12 are examined;
;if there are too many, we fall through...
A59824:
LD A,14 ;A=14 (BAD FILE NAME error)
OR A ;ZF=0 for error exit
POP HL
RET
;***************************************************************************
A59829:
LD A,C ;A=character count
CP 1 ;was it just 1?
JR Z,A59824 ;YES, so too few characters error
LD B,0 ;NO, string OK, so zero out B (BC=count)
XOR A ;A=0, ZF=1 for OK exit
POP HL
RET
;***************************************************************************
;SET UP NEW DIRECTORY ENTRY subroutine.
; On entry, IY=offset address of entry in DTA0. On exit, if the filename
; was too long or short, ZF=1 and A=14 (BAD FILENAME error). Otherwise,
; the new entry is written to DTA0 and ZF=1, A=0.
A59839:
PUSH IY ;save entry offset address...
POP DE ;and get it back in DE
LD HL,(USER_NAME) ;HL=address of file name string
CALL A59810 ;CHECK FILENAME LENGTH subroutine
JP NZ,A59903 ;too long or short, so error 14 exit
LDIR ;name OK, so move filename string to DTA0
;length in BC returned by check subroutine
LD A,16 ;file attribute=00010000 (user file)
LD (IY+12),A ;set it into directory entry
LD BC,(BLOCKS_REQ) ;BC=requested length of file
LD (IY+17),C
LD (IY+18),B ;set file length in dir entry
LD (IY+19),1
LD (IY+20),0 ;used length=1
LD (IY+21),0
LD (IY+22),0 ;last byte count=0
LD A,(EOS_YEAR) ;A=current file creation year
LD (IY+23),A ;set it into dir entry
LD A,(EOS_MONTH) ;A=current file creation month
LD (IY+24),A ;set it into dir entry
LD A,(EOS_DAY) ;A=current file creation day
LD (IY+25),A ;set it into dir entry
XOR A ;A=0, ZF=1 for OK exit
A59903:
RET
;***************************************************************************
;EOS Function 48: OPEN FILE.
; On entry, A=device number, HL=address of filename string, B=I/O mode.
; I/O mode decodes as follows: 1=read, 2=write, 3=random (read/write),
; 4=execute. The file must have already been created with Fn51 (create
; file). On exit, if the open was successful, ZF=1 and A=file number.
; If the file was not opened for write alone, the DTA contains the first
; block of the file. If the opened file is only 1 block long, bit 7 of
; the FCB I/O mode byte (24) is set. If the open was unsuccessful, ZF=0
; and A=error code.
__OPEN_FILE:
PUSH IY
PUSH HL
PUSH IX
PUSH DE
PUSH AF
PUSH BC
LD IX,(FCB_HEAD_ADDR) ;IX=address of FCB0
LD DE,35 ;length of FCB=35
ADD IX,DE ;point IX to FCB1
LD IY,(FCB_DATA_ADDR) ;IY=address of DTA0
LD DE,1024 ;length of DTA=1024
ADD IY,DE ;point to DTA1
LD B,1 ;file number 1
A59932:
LD A,(IX+24) ;get I/O mode byte from FCB
OR A ;is this FCB already in use?
JR Z,A59966 ;NO, so we'll use it
LD DE,35 ;YES, so point to next FCB and DTA
ADD IX,DE ;IX=address of next FCB
LD DE,1024
ADD IY,DE ;IY=address of next DTA
INC B ;next file number...
LD A,B ;in A
CP 3 ;is it 3?
JR C,A59932 ;NO, so we've still got FCBs to look at
POP BC ;YES, so all FCBs in use, error exit
POP AF
POP DE
POP IX
POP HL
POP IY
LD A,7 ;A=7 (TOO MANY OPEN FILES error)
OR A ;clear ZF for error exit
RET
;***************************************************************************
A59966:
LD (BUF_START),IY ;save DTA as buffer start
POP AF ;restore entry I/O mode (from BC)
LD (IX+24),A ;save it in FCB
POP AF ;restore device number
LD (IX+23),A ;save it in FCB
PUSH BC ;save file number (in B)
PUSH HL ;save filename string address...
POP DE ;and get it back in DE
PUSH IX ;save FCB address...
POP HL ;and get it back in HL
LD A,(IX+23) ;A=device #
CALL __QUERY_FILE ;*Fn52: FIND FILE (WITH TYPE)
JP NZ,A60152 ;file not found, so error exit ZF=0
CALL __MODE_CHECK ;found! so *Fn67: CHECK FILE I/O MODE
JP NZ,A60152 ;check failed, so error exit ZF=0
;***************************************************************************
;CALCULATE LAST BLOCK OF FILE.
; Adds used length (1 word, bytes 19-20) to start block (2 words, bytes
; 13-16) and saves 2-word result (bytes 29-32). Since this sum is the
; block AFTER the last block, 1 is subtracted from the 2 words at 29-32.
LD A,(IX+19) ;check OK, so get lobyte of used length
ADD A,(IX+13) ;add it to loword/lobyte of start block
LD (IX+29),A ;save it in FCB (last block loword/lobyte)
LD A,(IX+14) ;get loword/hibyte of start block
ADC A,(IX+20) ;add it to hibyte of used length
LD (IX+30),A ;save it in FCB (last block loword/hibyte)
LD A,(IX+15) ;get hiword/lobyte of start block
ADC A,0 ;propagate any carry
LD (IX+31),A ;save it in FCB (last block hiword/lobyte)
LD A,(IX+16) ;get hiword/hibyte of start block
ADC A,0 ;propagate any carry
LD (IX+32),A ;save it in FCB (last block hiword/hibyte)
LD A,(IX+29) ;get last file block loword/lobyte
SUB 1 ;subtract 1
LD (IX+29),A ;save it back in FCB
LD A,(IX+30) ;get last file block loword/hibyte
SBC A,0 ;propagate any carry
LD (IX+30),A ;save it back in FCB
LD A,(IX+31) ;get last file block hiword/lobyte
SBC A,0 ;propagate any carry
LD (IX+31),A ;save it back in FCB
LD A,(IX+32) ;get last file block hiword/hibyte
SBC A,0 ;propagate any carry
LD (IX+32),A ;save it back in FCB
;***************************************************************************
LD DE,(BUF_START) ;get buffer start address (actually DTAn)
LD (IX+33),E
LD (IX+34),D ;save it in FCB
LD A,(IX+13) ;get loword/lobyte of start block
LD (IX+25),A ;save it in FCB
LD A,(IX+14) ;get loword/hibyte of start block
LD (IX+26),A ;save it in FCB
LD A,(IX+15) ;get hiword/lobyte of start block
LD (IX+27),A ;save it in FCB
LD A,(IX+16) ;get hiword/hibyte of start block
LD (IX+28),A ;save it in FCB
LD A,(IX+24) ;get I/O mode byte
AND 7 ;mask out bits 7-3
CP 2 ;is it 2? (open for write alone)
JR Z,A60147 ;YES, so exit without reading 1st block
XOR A ;NO, so A=0
CP (IX+20) ;is hibyte of used length=0?
;(used length less than 256 blocks?)
JR NZ,A60124 ;YES, so read in 1st block of file
INC A ;NO, so A=1
CP (IX+19) ;is lobyte of used length=1?
;(file is [256*n]+1 blocks long, n>0)
JR NZ,A60124 ;NO, so read in 1st block of file
SET 7,(IX+24) ;YES, so set bit 7 of I/O mode byte
;before reading 1st block of file
;(last physical block of file is in DTA)
A60124:
LD A,(IX+23) ;A=device #
LD HL,(BUF_START) ;HL=buffer start address
LD E,(IX+25)
LD D,(IX+26)
LD C,(IX+27)
LD B,(IX+28) ;BCDE=block number to read
CALL __READ_BLOCK ;*Fn65: READ BLOCK
JR NZ,A60152 ;read failed, so error exit ZF=0
A60147:
XOR A ;A=0 for OK exit
POP BC ;restore file number (in B)
LD A,B ;pass it back from subroutine in A
JR A60157 ;exit OK ZF=1
;***************************************************************************
A60152:
LD (IX+24),0 ;zero out I/O mode byte (disallocate FCB)
POP BC
A60157:
POP DE
POP IX
POP HL
POP IY
RET
;***************************************************************************
;EOS Function 49: CLOSE FILE.
; On entry, A=file number. On exit, if the close was successful, ZF=1 and
; A=0. If the file was opened for writing, the contents of the DTA are
; written to the file, and the directory entry updated. If the close was
; unsuccessful, ZF=0 and A=error code.
__CLOSE_FILE:
PUSH IX
PUSH HL
PUSH DE
PUSH BC
OR A ;file number 0?
JR Z,A60252 ;YES, so BAD FILE NUMBER error
CP 3 ;file number less than 3?
JR NC,A60252 ;NO, so BAD FILE NUMBER error
LD B,A ;YES, file number OK, so save it in B
LD IX,(FCB_HEAD_ADDR) ;IX=address of FCB0
LD HL,(FCB_DATA_ADDR) ;HL=address of DTA0
A60184:
LD DE,35 ;length of FCB=35
ADD IX,DE ;IX=address of next FCB
LD DE,1024 ;length of DTA
ADD HL,DE ;point HL to next file DTA
DJNZ A60184 ;keep going 'til we're there
LD A,(IX+24) ;we're there! A=I/O mode byte from FCB
OR A ;is it zero? (FCB not in use)
JR Z,A60252 ;YES, so no file to close! exit error
AND 64 ;NO, there's a file to close; bit 6 set?
;(data in DTA waiting to be written)
JR Z,A60242 ;NO, so just disallocate FCB and exit
LD (FILE_NAME_ADDR),IX ;YES, so we have to write the last block
;to the file.
;set address of filename at start of FCB
LD A,(IX+23) ;A=device #
LD E,(IX+25)
LD D,(IX+26)
LD C,(IX+27)
LD B,(IX+28) ;BCDE=block number to write
CALL __WRITE_BLOCK ;*Fn66: WRITE BLOCK
JR NZ,A60261 ;write failed, so error exit ZF=0
LD A,(IX+23) ;write OK, so restore A=device #
PUSH IX ;save FCB address...
POP HL ;and get it back in HL
LD DE,(FILE_NAME_ADDR) ;DE=address of filename
CALL __SET_FILE ;*Fn53: UPDATE DIRECTORY ENTRY
A60242:
LD (IX+24),0 ;disallocate FCB by zeroing out I/O mode
POP BC
POP DE
POP HL
POP IX
RET
;***************************************************************************
A60252:
POP BC
POP DE
POP HL
POP IX
LD A,9 ;A=9 (BAD FILE NUMBER error)
OR A ;clear ZF for error exit
RET
;***************************************************************************
A60261:
POP BC
POP DE
POP HL
POP IX
OR A ;clear ZF for other error exit (A=code)
RET
;***************************************************************************
;EOS Function 50: RESET FILE.
; On entry, A=file number. On exit, if reset was successful, ZF=1 and A=0.
; The current DTA is written to the file (if opened for write), and then
; the first block of the file is read into the DTA (if not opened for
; write alone). If reset failed, ZF=0 and A=error code.
__RESET_FILE:
PUSH IX
PUSH DE
PUSH BC
PUSH HL
OR A ;file number zero?
JP Z,A60423 ;YES, so BAD FILE NUMBER error
CP 3 ;file number less than 3?
JP NC,A60423 ;NO, so BAD FILE NUMBER error
LD B,A ;YES, file number OK, so save it in B
LD IX,(FCB_HEAD_ADDR) ;IX=address of FCB0
LD HL,(FCB_DATA_ADDR) ;HL=address of DTA0
A60290:
LD DE,35 ;length of FCB=35
ADD IX,DE ;point IX to next FCB
LD DE,1024 ;length of DTA=1024
ADD HL,DE ;point HL to next file DTA
DJNZ A60290 ;keep going 'til we're there
LD A,(IX+24) ;we're there! A=I/O mode byte from FCB
OR A ;is it zero? (FCB not in use)
JR Z,A60423 ;YES, so no file to reset! error exit
AND 64 ;NO, there's a file to reset; bit 6 set?
;(data in DTA waiting to be written)
JR Z,A60335 ;NO, so skip ahead
LD A,(IX+23) ;YES, so write the data; A=device #
LD E,(IX+25)
LD D,(IX+26)
LD C,(IX+27)
LD B,(IX+28) ;BCDE=block number to write
CALL __WRITE_BLOCK ;*Fn66: WRITE BLOCK
JR NZ,A60432 ;write failed, so error exit ZF=0
RES 6,(IX+24) ;write OK, so clear bit 6
;(no data waiting to write)
A60335:
RES 7,(IX+24) ;clear bit 7
;(last block of file not in DTA)
LD A,(IX+13) ;get loword/lobyte of start block
LD (IX+25),A ;save it in FCB
LD A,(IX+14) ;get loword/hibyte of start block
LD (IX+26),A ;save it in FCB
LD A,(IX+15) ;get hiword/lobyte of start block
LD (IX+27),A ;save it in FCB
LD A,(IX+16) ;get hiword/hibyte of start block
LD (IX+28),A ;save it in FCB
LD (IX+33),L
LD (IX+34),H ;save DTA (from HL) in FCB
LD A,(IX+24) ;get I/O mode byte
AND 7 ;mask out upper 5 bits
CP 2 ;is it 2? (open for write alone)
JR NZ,A60396 ;NO, so read in first block of the file
LD (IX+21),0 ;YES,so...
LD (IX+22),0 ;set last byte to zero
LD (IX+19),1
LD (IX+20),0 ;set used length to 1 block
JR A60416 ;skip first block read and exit OK
;***************************************************************************
A60396:
LD A,(IX+23) ;A=device #
LD E,(IX+25)
LD D,(IX+26)
LD C,(IX+27)
LD B,(IX+28) ;BCDE=block number to read
CALL __READ_BLOCK ;*Fn65: READ BLOCK
JR NZ,A60432 ;read failed, so error exit ZF=0
A60416:
POP HL ;read OK, so exit
POP BC
POP DE
POP IX
XOR A ;A=0, ZF=1 for OK exit
RET
;***************************************************************************
A60423:
POP HL
POP BC
POP DE
POP IX
LD A,9 ;A=9 (BAD FILE NUMBER error)
OR A ;clear ZF for error exit
RET
;***************************************************************************
A60432:
POP HL
POP BC
POP DE
POP IX
OR A ;clear ZF for error exit (A=code)
RET
;***************************************************************************
;EOS Function 54: READ FILE.
; On entry, A=file number, BC=number of bytes to read from the file, HL=
; address of read buffer to receive the data (not the same as file manager
; DTA). The file must already have been opened by Fn48 (open file). On
; exit, if the read was successful, ZF=1, A=0, BC=same as entry, and FCB
; bytes 33-34 point to the end of the read buffer. Otherwise, ZF=0, A=
; error code. If A=9 (BAD FILE NUMBER) or A=10 (INPUT PAST END), BC=number
; of bytes actually read from the file; for other errors, BC is unknown.
__READ_FILE:
PUSH BC
PUSH DE
PUSH HL
PUSH IX
LD (USER_BUF),HL ;save read buffer address
LD (BYTES_REQ),BC ;save bytes requested to read
LD (FNUM),A ;save file number
LD (BYTES_TO_GO),BC ;set bytes left to read=bytes requested
OR A ;file number zero?
JP Z,A60799 ;YES, so BAD FILE NUMBER error
CP 3 ;file number less than 3?
JP NC,A60799 ;NO, so BAD FILE NUMBER error
LD B,A ;YES, so B=file number (offset counter)
LD IX,(FCB_HEAD_ADDR) ;IX=address of FCB0
LD HL,(FCB_DATA_ADDR) ;HL=address of DTA0
A60475:
LD DE,35 ;length of FCB=35
ADD IX,DE ;point IX to next FCB
LD DE,1024 ;length of DTA=1024
ADD HL,DE ;point HL to next file DTA
DJNZ A60475 ;keep going 'til we're there
LD (BUF_START),HL ;we're here! save DTA as buffer start addr
ADD HL,DE ;compute buffer end address...
LD (BUF_END),HL ;and save it (buffer is 1024 bytes long)
PUSH IX ;save FCB address...
POP HL ;and get it back in HL
CALL __MODE_CHECK ;*Fn67: CHECK FILE I/O MODE
JP NZ,A60801 ;check failed, so error exit ZF=0
;***************************************************************************
;COPY DATA FROM DTA TO READ BUFFER.
; Data is copied 1024 bytes at a time, except when fewer are requested.
; At the end each 1024-byte transfer, the read buffer start address (65030)
; is adjusted upward for the next copy.
A60502:
BIT 7,(IX+24) ;check OK; was bit 7 of I/O mode byte set?
;(last physical block of file in DTA)
JP NZ,A60715 ;YES, so INPUT PAST END CHECK
LD HL,(BUF_END) ;NO, so HL=buffer end address
LD E,(IX+33)
LD D,(IX+34) ;DE=DTA start from FCB
OR A ;clear CF
SBC HL,DE ;HL=buffer size (end-start); is it zero?
JR Z,A60560 ;YES, so we're done with this block
;read in the next one
LD B,H ;NO, so keep working with current block
LD C,L ;BC=HL (buffer size)
LD DE,(BYTES_TO_GO) ;DE=bytes left to read
OR A ;clear CF
SBC HL,DE ;compare buffer size with bytes left:
;are there more bytes than buffer?
JP NC,A60650 ;NO, so read the last bytes and exit
LD HL,(BYTES_TO_GO) ;YES, so restore HL=to bytes left to read
OR A ;clear CF
SBC HL,BC ;HL=new bytes left (old-buffer size)
LD (BYTES_TO_GO),HL ;save new bytes left
LD L,(IX+33)
LD H,(IX+34) ;HL=DTA from FCB
LD DE,(USER_BUF) ;DE=read buffer address
LDIR ;move BC bytes from DTA to read buffer
;(remember, BC=buffer size)
LD (USER_BUF),DE ;set new read buffer start=old end
;***************************************************************************
;READ NEXT BLOCK OF FILE.
; This only occurs if more than 1024 bytes are requested from the file.
A60560:
LD HL,(BUF_START) ;HL=buffer start address
LD (IX+33),L
LD (IX+34),H ;store buffer start address in FCB
INC (IX+25) ;point to next block of file
JR NZ,A60587 ;we can stop if not zero
INC (IX+26) ;sorry, must propagate carry
JR NZ,A60587 ;we can stop if not zero
INC (IX+27) ;sorry, must propagate carry
JR NZ,A60587 ;we can stop if not zero
INC (IX+28) ;sorry, must propagate carry
A60587:
LD A,(IX+23) ;A=device #
LD HL,(BUF_START) ;HL=buffer start address
LD E,(IX+25)
LD D,(IX+26)
LD C,(IX+27)
LD B,(IX+28) ;BCDE=block number to read
CALL __READ_BLOCK ;*Fn65: READ BLOCK
JP NZ,A60801 ;read failed, so error exit ZF=0
LD A,(IX+32) ;read OK, get hiword/hibyte of last block
CP (IX+28) ;is it same as current?
JR NZ,A60647 ;NO, so more blocks to read
LD A,(IX+31) ;YES, so get hiword/lobyte of last block
CP (IX+27) ;is it same as current?
JR NZ,A60647 ;NO, so more blocks to read
LD A,(IX+30) ;YES, so get loword/hibyte of last block
CP (IX+26) ;is it same as current?
JR NZ,A60647 ;NO, so more blocks to read
LD A,(IX+29) ;YES, so get loword/lobyte of last block
CP (IX+25) ;is it same as current?
JR NZ,A60647 ;NO, so more blocks to read
SET 7,(IX+24) ;YES, so the last physical block of the
;file has been read in.
;set bit 7 of I/O mode byte to flag this
A60647:
JP A60502 ;keep reading 'til all bytes read
;***************************************************************************
;COPY LAST 1024 BYTES (OR LESS) OF DATA TO READ BUFFER.
A60650:
LD A,(IX+32) ;get hiword/hibyte of last block
CP (IX+28) ;is it same as current?
JR NZ,A60682 ;NO, so copy rest of data
LD A,(IX+31) ;YES, so get hiword/lobyte of last block
CP (IX+27) ;is it same as current?
JR NZ,A60682 ;NO, so copy rest of data
LD A,(IX+30) ;YES, so get loword/hibyte of last block
CP (IX+26) ;is it same as current?
JR NZ,A60682 ;NO, so copy rest of data
LD A,(IX+29) ;YES, so get loword/lobyte of last block
CP (IX+25) ;is it same as current?
JR Z,A60711 ;NO, but it's the last block we'll read
;copy and exit
A60682:
LD BC,(BYTES_TO_GO) ;YES, it's the last block, BC=bytes left
LD L,(IX+33)
LD H,(IX+34) ;HL=transfer start from FCB
LD DE,(USER_BUF) ;DE=read buffer start address
LDIR ;move rest of data to read buffer
LD (IX+33),L
LD (IX+34),H ;set FCB pointer to end of read buffer
POP IX
POP HL
POP DE
POP BC
XOR A ;A=0, ZF=1 for OK exit
RET
;***************************************************************************
;INPUT PAST END CHECK.
; If more bytes have been requested than the file is long, error 10 (I/O
; PAST END) is returned, with BC=number of bytes actually read.
A60711:
SET 7,(IX+24) ;set bit 7 of I/O mode byte
;(last physical block of file is in DTA)
A60715:
LD HL,(BUF_START) ;HL=buffer start address
LD E,(IX+21)
LD D,(IX+22) ;DE=last byte count from FCB
ADD HL,DE ;HL=computed address of buffer end
LD E,(IX+33)
LD D,(IX+34) ;DE=transfer start address from FCB
OR A ;clear CF
SBC HL,DE ;HL=number of bytes left to copy
LD B,H
LD C,L ;save byte count in BC; was it zero?
JR Z,A60780 ;YES, so exit error 10 (I/O PAST END)
LD DE,(BYTES_TO_GO) ;NO, so get bytes left to read
OR A ;clear CF
SBC HL,DE ;is the # of bytes to copy from DTA less
;than total # of bytes left to read?
JR NC,A60682 ;NO (probably equal), so finish copy and
;exit OK
LD HL,(BYTES_TO_GO) ;YES, but this is an error; copy what data
;we have to read buffer and exit
;reset HL=bytes left to read
OR A ;clear CF
SBC HL,BC ;(old total bytes left)-(bytes left in
;1024-byte buffer)=(new total bytes left)
LD (BYTES_TO_GO),HL ;save new bytes left to read
LD L,(IX+33)
LD H,(IX+34) ;get transfer start from FCB
LD DE,(USER_BUF) ;DE=read buffer address
LDIR ;copy data to from DTA to read buffer
LD HL,(BYTES_REQ) ;HL=bytes requested to read
LD BC,(BYTES_TO_GO) ;BC=bytes left to read
OR A ;clear CF
SBC HL,BC ;HL=number of bytes actually read
LD B,H
LD C,L ;save it in BC
A60780:
LD A,10 ;A=10 (I/O PAST END error)
A60782:
POP IX
POP HL
POP DE
INC SP ;get entry BC off stack...
INC SP ;without disturbing exit BC
OR A ;clear zero flag for return status
RET ;EXIT
;***************************************************************************
;UNUSED ERROR HANDLER.
; This routine returns error code 21, but it is not called anywhere in
; EOS-5. What the error signifies is unknown, though presumably it has
; something to do with file I/O.
A60790:
POP IX
POP HL
POP DE
POP BC
LD A,21 ;A=21 (unknown error code)
OR A ;clear ZF for error exit
RET ;EXIT
;***************************************************************************
A60799:
LD A,9 ;A=9 (BAD FILE NUMBER error)
A60801:
LD HL,(BYTES_REQ) ;HL=bytes requested to read
LD BC,(BYTES_TO_GO) ;BC=bytes left to read
OR A ;clear CF
SBC HL,BC ;HL=number of bytes actually read
LD B,H
LD C,L ;save it in BC
JR A60782 ;error exit
;***************************************************************************
;EOS Function 55: WRITE FILE.
; On entry, A=file number, BC=number of bytes to write to the file, HL=
; address of write buffer to send the data (not the same as file manager
; DTA). The file must already have been opened by Fn48 (open file). On
; exit, if the write was successful, ZF=1 and A=0. Otherwise, ZF=0 and
; A=error code.
__WRITE_FILE:
PUSH BC
PUSH DE
PUSH HL
PUSH IX
LD (USER_BUF),HL ;save write buffer address
LD (BYTES_REQ),BC ;save bytes requested to write
LD (FNUM),A ;save file number
LD (BYTES_TO_GO),BC ;bytes left to write=bytes requested
OR A ;file number zero?
JP Z,A61116 ;YES, so BAD FILE NUMBER error
CP 3 ;file number less than 3?
JP NC,A61116 ;NO, so BAD FILE NUMBER error
LD B,A ;B=file number
LD IX,(FCB_HEAD_ADDR) ;IX=address of FCB0
LD DE,35 ;length of FCB=35
A60851:
ADD IX,DE ;point IX to next FCB
DJNZ A60851 ;offset until IX=addr of correct FCB
LD B,A ;set B back to file number
LD DE,1024 ;length of DTA
LD HL,(FCB_DATA_ADDR) ;HL=address of DTA0
A60862:
ADD HL,DE ;point HL to next file DTA
DJNZ A60862 ;offset until correct file DTA is reached
LD (BUF_START),HL ;save it as write buffer start address
ADD HL,DE ;length=1024
LD (BUF_END),HL ;save write buffer end address
PUSH IX ;save FCB address...
POP HL ;and get it back in HL
CALL __MODE_CHECK ;*Fn67: CHECK FILE I/O MODE
JP NZ,A61118 ;check failed, so error exit ZF=0
;***************************************************************************
;COPY DATA FROM WRITE BUFFER TO DTA.
; Data is copied 1024 bytes at a time, except when fewer are requested. At
; the end each 1024-byte transfer, the write buffer start address (65030)
; is adjusted upward for the next copy.
A60881:
LD A,(IX+20) ;check OK, so get hibyte of used length
CP (IX+18) ;is it less than allocated length?
JR C,A60901 ;YES, so not last block of file
LD A,(IX+19) ;NO, so get lobyte of used length
CP (IX+17) ;is it less than allocated length?
JR C,A60901 ;YES, so not last block of file
SET 7,(IX+24) ;NO, so this is the last available block;
;set bit 7 of I/O mode byte to flag this
A60901:
LD HL,(BUF_END) ;HL=write buffer end address
LD E,(IX+33)
LD D,(IX+34) ;DE=DTA offset from FCB
OR A ;clear CF
SBC HL,DE ;HL=room left in DTA
LD B,H
LD C,L ;save it in BC
LD DE,(BYTES_TO_GO) ;DE=bytes left to write
OR A ;clear CF
SBC HL,DE ;are there more bytes left to write from
;the write buffer than there is room in
;the DTA?
JP NC,A61075 ;NO, so write the last block and exit OK
LD A,B ;YES, so more than 1 block left to write
OR C ;is BC=0? (no more room in DTA)
JR Z,A60956 ;YES, so write the block, reset and cont.
LD HL,(BYTES_TO_GO) ;NO, still room, so HL=bytes left to write
OR A ;clear CF
SBC HL,BC ;compute new bytes left to write...
LD (BYTES_TO_GO),HL ;and save it
LD E,(IX+33)
LD D,(IX+34) ;get DTA offset address from FCB
LD HL,(USER_BUF) ;HL=address of write buffer
LDIR ;move last bytes from write buffer to DTA
LD (USER_BUF),HL ;reset HL to write buffer offset
SET 6,(IX+24) ;set bit 6 of I/O mode byte
;(data waiting to write)
A60956:
BIT 7,(IX+24) ;is bit 7 of I/O mode byte set?
;(last physical block of file in DTA)
JR Z,A60971 ;NO, so write the block and continue
POP IX ;YES, the file is too short, so error exit
POP HL
POP DE
POP BC
LD A,10 ;A=10 (I/O PAST END error)
OR A ;clear ZF for error exit
RET
;***************************************************************************
;WRITE BLOCK TO FILE AND POINT TO NEXT BLOCK.
A60971:
INC (IX+19) ;increment used length lobyte; any carry?
JR NZ,A60979 ;NO, so continue
INC (IX+20) ;YES, so add carry to used length hibyte
A60979:
LD A,(IX+23) ;A=device number
LD HL,(BUF_START) ;HL=buffer start address
LD E,(IX+25)
LD D,(IX+26)
LD C,(IX+27)
LD B,(IX+28) ;BCDE=block number to write
CALL __WRITE_BLOCK ;*Fn66: WRITE BLOCK
JP NZ,A61118 ;write failed, so error exit ZF=0
RES 6,(IX+24) ;write OK, so clear bit 6 of I/O mode byte
;(no data waiting to write)
LD HL,(BUF_START) ;HL=buffer start address
LD (IX+33),L
LD (IX+34),H ;store buffer start address in FCB
LD (IX+21),0
LD (IX+22),0 ;zero out last byte counter
INC (IX+25) ;increment loword/lobyte of next block
JR NZ,A61042 ;no carry, so continue
INC (IX+26) ;carry, so add it to loword/hibyte
JR NZ,A61042 ;no carry, so continue
INC (IX+27) ;carry, so add it to hiword/lobyte
JR NZ,A61042 ;no carry, so continue
INC (IX+28) ;carry, so add it to hiword/hibyte
A61042:
LD A,(IX+24) ;A=I/O mode byte from FCB
AND 7 ;mask out upper 5 bits
CP 3 ;is it 3? (open for read+write)
JP NZ,A60881 ;NO, so COPY DATA FROM WRITE BUFFER TO DTA
;***************************************************************************
;APPEND DATA TO FILE.
LD A,(IX+23) ;YES, so read next block of file
;and append data to it; A=dev #
LD HL,(BUF_START) ;HL=write buffer start address
LD E,(IX+25)
LD D,(IX+26)
LD C,(IX+27)
LD B,(IX+28) ;BCDE=block number to read
CALL __READ_BLOCK ;*Fn65: READ BLOCK
JR NZ,A61118 ;read failed, so error exit ZF=0
A61075:
LD BC,(BYTES_TO_GO) ;BC=bytes left to write
LD L,(IX+21)
LD H,(IX+22) ;HL=last byte count for current block
ADD HL,BC ;HL=bytes left+block last byte count
LD (IX+21),L
LD (IX+22),H ;save new last byte count
LD E,(IX+33)
LD D,(IX+34) ;DE=target transfer address from FCB
LD HL,(USER_BUF) ;HL=address of user buffer
LDIR ;copy bytes left from write buffer to DTA
LD (IX+33),E
LD (IX+34),D ;set FCB data to end-of-transfer address
SET 6,(IX+24) ;set bit 6 of I/O mode byte
;(data waiting to write)
XOR A ;A=0, ZF=1 for OK exit
JR A61119 ;skip over error handler
;***************************************************************************
A61116:
LD A,9 ;A=9 (BAD FILE NUMBER error)
A61118:
OR A ;clear ZF for error exit
A61119:
POP IX
POP HL
POP DE
POP BC
RET
;***************************************************************************
;EOS Function 56: SET CURRENT DATE.
; On entry, B=current day, C=current month, D=current year.
__SET_DATE:
PUSH AF
LD A,B
LD (EOS_DAY),A ;set current day
LD A,C
LD (EOS_MONTH),A ;set current month
LD A,D
LD (EOS_YEAR),A ;set current year
POP AF
RET
;***************************************************************************
;EOS Function 57: GET CURRENT DATE.
; On exit, B=current day, C=current month, D=current year. If no date has
; been set (0/0/0), ZF=0 and A=4 (NO DATE SET error). Otherwise, ZF=1 and
; A=0.
__GET_DATE:
LD A,(EOS_DAY) ;get current day
LD B,A ;into B
LD A,(EOS_MONTH) ;get current month
LD C,A ;into C
LD A,(EOS_YEAR) ;get current year
LD D,A ;into D
OR B
OR C ;was there a date set?
JR Z,A61158 ;NO (all 3 zero)
XOR A ;YES, so A=0, ZF=1 for OK exit
RET
;***************************************************************************
A61158:
LD A,4 ;A=4 (NO DATE SET error)
OR A ;ZF=0 for error exit
RET
;***************************************************************************
;EOS Function 46: INITIALIZE FILE MANAGER.
; On entry, DE=address of DTA0, HL=address of FCB0. On exit, the I/O mode
; byte (24) of FCB0, FCB1 and FCB2 is set to zero.
__FMGR_INIT:
LD (FCB_DATA_ADDR),DE ;address of DTA0=DE
LD (FCB_HEAD_ADDR),HL ;address of FCB0=HL
PUSH BC
PUSH DE
PUSH IX
LD B,3 ;loop 3 times
LD DE,35 ;length of FCB=35
LD IX,(FCB_HEAD_ADDR) ;IX=address of FCB0
A61182:
LD (IX+24),0 ;zero out I/O mode byte
ADD IX,DE ;point IX to next FCB
DJNZ A61182 ;loop back 3 times
POP IX
POP DE
POP BC
RET
;***************************************************************************
;EOS Function 68: READ DIRECTORY FOR FILE.
; On entry, A=device number, HL=address of filename string. On exit, if
; file was found, ZF=1, A=0, BCDE=start block of file, and FCB0 bytes 33-34
; contain the address of the matching entry in DTA0. Otherwise, ZF=0 and
; A=error code.
__SCAN_FOR_FILE:
PUSH HL
PUSH IX
PUSH IY
LD (USER_NAME),HL ;save address of file name string
LD IX,(FCB_HEAD_ADDR) ;IX=address of FCB0
LD (IX+23),A ;set device #
LD (IX+25),1 ;block to read=1 (directory)
LD (IX+26),0
LD (IX+27),0
LD (IX+28),0
LD DE,(FCB_DATA_ADDR) ;DE=address of DTA0
LD (IX+33),E
LD (IX+34),D ;save it in FCB0 (current DTA offset addr)
LD HL,FILE_COUNT ;HL=address of file count
LD (HL),0 ;zero out count
LD A,(IX+23) ;A=device #
LD HL,(FCB_DATA_ADDR) ;HL=address of DTA0
LD E,(IX+25)
LD D,(IX+26)
LD C,(IX+27)
LD B,(IX+28) ;BCDE=block to read
CALL __READ_BLOCK ;*Fn65: READ BLOCK
JP NZ,A61478 ;read failed, so error exit ZF=0
PUSH HL ;save DTA offset address...
POP IY ;and get it back in IY
;we should be pointing at VOLUME entry
LD A,(IY+12) ;A=volume size byte
AND 127 ;mask out bit 8
LD (IX+29),A ;save volume size loword/lobyte in FCB0
LD (IX+30),0 ;zero out loword/hibyte
LD (IX+31),0 ;zero out hiword/lobyte
LD (IX+32),0 ;zero out hiword/hibyte
CALL A61493 ;VERIFY DIRECTORY CHECK CODE subroutine
JP NZ,A61478 ;bad code, so error exit ZF=0
LD A,(IX+33) ;code OK, so get lobyte of current DTA
;offset address
ADD A,26 ;point ahead to next dir entry (DIRECTORY)
LD (IX+33),A ;save new lobyte in FCB0
LD A,(IX+34) ;get hibyte
ADC A,0 ;propagate any carry from lobyte add
LD (IX+34),A ;save new hibyte in FCB0
LD HL,FILE_COUNT ;HL=address of file count
INC (HL) ;we've looked at first entry
LD B,38 ;38 more to look at
JR A61393 ;keep looking
;***************************************************************************
;READ DIRECTORIES WITH MORE THAN 1 BLOCK.
; On entry, bytes 32-29 of FCB0 contain the maximum directory size. Since
; the directory also begins at block 1, this is also the last directory
; block number.
A61318:
INC (IX+25) ;point to next directory block to read
;NOTE: any carry is not propagated through
;other 3 bytes, thus 255+1=0
;thus physical max dirsize=255 blocks
LD A,(IX+32) ;get max block hiword/hibyte
CP (IX+28) ;is it less than next block?
JP C,A61484 ;YES, so error 5 exit
LD A,(IX+31) ;NO, so get max block hiword/lobyte
CP (IX+27) ;is it less than next block?
JP C,A61484 ;YES, so error 5 exit
LD A,(IX+30) ;NO, so get max block loword/hibyte
CP (IX+26) ;is it less than next block?
JP C,A61484 ;YES, so error 5 exit
LD A,(IX+29) ;NO, so get max block loword/lobyte
CP (IX+25) ;is it less than next block?
JP C,A61484 ;YES, so error 5 exit
LD A,(IX+23) ;NO, next block OK, get device # from FCB0
LD HL,(FCB_DATA_ADDR) ;HL=address of DTA0
LD E,(IX+25)
LD D,(IX+26)
LD C,(IX+27)
LD B,(IX+28) ;BCDE=next block to read
CALL __READ_BLOCK ;*Fn65: READ BLOCK
JP NZ,A61478 ;read failed, so error exit ZF=0
LD DE,(FCB_DATA_ADDR) ;DE=address of DTA0
LD (IX+33),E
LD (IX+34),D ;save it in FCB0
LD B,39 ;39 files to read
;***************************************************************************
A61393:
LD HL,FILE_COUNT ;HL=address of file count
INC (HL) ;count to next file
LD E,(IX+33)
LD D,(IX+34) ;DE=DTA offset addr (current entry start)
LD L,12
LD H,0 ;HL=12
ADD HL,DE ;offset to attribute byte
BIT 0,(HL) ;is bit 0 set? (NOT A FILE)
JR NZ,A61484 ;YES, so BLOCKS LEFT and end of directory
;error exit ZF=0
BIT 2,(HL) ;is bit 2 set? (DELETED FILE)
JR NZ,A61435 ;YES, file is deleted, so skip over it
LD HL,(USER_NAME) ;NO, it's a real file, so get address of
;file name string
LD A,(FILENAME_CMPS) ;A=file name comparison byte
OR A ;is it zero?
JR Z,A61430 ;YES, so COMPARE FILE NAMES WITH TYPE sub
CALL A61524 ;NO, so COMPARE FILE NAMES WITHOUT TYPE
JR A61433 ;skip COMPARE WITH TYPE subrt and continue
;***************************************************************************
A61430:
CALL A61523 ;COMPARE FILE NAMES WITH TYPE subroutine
A61433:
JR Z,A61456 ;names match! so get start block and exit
A61435:
LD A,(IX+33) ;no match, look again; A=DTA offset lobyte
ADD A,26 ;point to next directory entry
LD (IX+33),A ;save new lobyte in FCB0
LD A,(IX+34) ;get hibyte
ADC A,0 ;propagate any carry from lobyte add
LD (IX+34),A ;save new hibyte
DJNZ A61393 ;keep looking 'til all 38 are looked at
JP A61318 ;we reached the end of the directory
;block without finding BLOCKS LEFT, so
;directory must have multiple blocks.
;get next directory block and keep looking
;***************************************************************************
A61456:
PUSH DE ;match! save addr of file entry in DTA...
POP IY ;and get it back in IY
LD E,(IY+13)
LD D,(IY+14)
LD C,(IY+15)
LD B,(IY+16) ;BCDE=starting block from directory entry
POP IY
POP IX
POP HL
XOR A ;A=0, ZF=1
RET ;exit OK
;***************************************************************************
A61478:
POP IY
POP IX
POP HL
RET ;error exit with ZF=0, A=error code
;***************************************************************************
A61484:
POP IY
POP IX
POP HL
LD A,5 ;A=5 (NO MORE DIRECTORY error)
;this could also be called FILE NOT FOUND
OR A ;clear ZF
RET
;***************************************************************************
;VERIFY DIRECTORY CHECK CODE subroutine.
; On entry, IY=base of DTA0, which contains the first directory block. On
; exit, if check code is 55AA00FF, ZF=1 and A=0. Otherwise, ZF=1 and A=24
; (NON-EOS VOLUME error).
A61493:
PUSH IY
PUSH BC
PUSH HL
LD HL,A62439 ;HL=addr of 4-byte code in volume entry
;in disk initialization data table
LD B,4 ;B=number of bytes to compare
A61502:
LD A,(HL) ;get a byte from the data table
CP (IY+13) ;does it match what's in DTA0?
JR NZ,A61519 ;NO, so error 24 exit
INC HL ;YES, so point to next byte in data table
INC IY ;point to next byte in DTA0
DJNZ A61502 ;keep comparing 'til all 4 bytes checked
XOR A ;they match! OK exit A=0, ZF=1
A61514:
POP HL
POP BC
POP IY
RET
;***************************************************************************
A61519:
LD A,24 ;no match! A=24 (NON-EOS VOLUME), ZF=0
JR A61514 ;error exit
;***************************************************************************
;FILENAME COMPARISON subroutines.
; On entry, HL=filename address, DE=address of directory entry in DTA0. On
; exit, if the names match, ZF=1 and A=0. Otherwise, ZF=0 and A=8 (MATCH
; NOT FOUND error). Unfortunately, this routine has 2 entry points with
; overlapping reading frames.
; (1) Return error if any mismatched characters, including filetype. Fn52
; (find file, with type) results in a call to this routine. Enters at
; 61523:
;61523 E637 AND 55 ;clears CF; continue at 61525
; (2) Allows the filetype byte to mismatch; otherwise returns error. Fn69
; (find file, no type) results in a call to this routine. Enters at
; 61524:
;61523 E6 ;unused
;61524 37 SCF ;sets CF; continues at 61525
A61523:
DB 230
A61524:
SCF
PUSH HL
PUSH DE
PUSH BC
PUSH AF ;save status of CF
LD B,12 ;B=number of bytes to compare
A61531:
LD A,(HL) ;get character from string
CP 3 ;is it the end? (hex 03=logical end)
JR Z,A61559 ;YES, so see if the dir entry also ends
LD A,(DE) ;NO, so get character from dir entry
CP (HL) ;does it match the string?
JR NZ,A61547 ;NO, but that may not be fatal...
INC HL ;YES, so point to next character in string
INC DE ;point to next character in dir entry
DJNZ A61531 ;keep going 'til all are compared
POP BC ;all 12 match, so exit OK
JR A61565
;***************************************************************************
A61547:
POP AF ;restore status of CF
JR NC,A61570 ;CF clear (strict match), so exit error 8
INC HL ;CF set (type can mismatch), so point to
;next character in string
INC DE ;point to next character in dir entry
LD A,(HL) ;get character from string
CP 3 ;is it the end?
JR NZ,A61570 ;NO, so exit error 8
JR A61560 ;YES, skip to see if dir entry also ends
;***************************************************************************
A61559:
POP BC
A61560:
LD A,(DE) ;get character from directory entry
CP 3 ;is it the end?
JR NZ,A61570 ;NO, so exit error 8
A61565:
POP BC ;YES, so exit OK
POP DE
POP HL
XOR A ;A=0, ZF=1
RET ;exit OK
;***************************************************************************
A61570:
POP BC
POP DE
POP HL
LD A,8 ;A=8 (MATCH NOT FOUND error)
OR A ;clear ZF
RET ;exit error
;***************************************************************************
;EOS Function 64: CHECK IF FILE IS OPEN.
; On entry, HL=address of file name string. On exit, if the file is open,
; ZF=1, A=0 and B=lower 3 bits of the I/O mode byte from the FCB (read,
; write, execute). Otherwise, ZF=0 and A=5 (FILE NOT OPEN error).
__CHECK_FCB:
PUSH IX
PUSH DE
PUSH HL ;save filename string address
LD HL,(FCB_HEAD_ADDR) ;HL=address of FCB0
XOR A ;A=0
A61585:
INC A ;increment file number counter
LD (FILE_NUMBR),A ;save new file number
LD DE,35 ;length of FCB=35
ADD HL,DE ;point to next FCB
PUSH HL ;save address...
POP IX ;and get it back in IX
LD A,(IX+24) ;A=I/O mode byte
AND 7 ;mask out upper 5 bits
CP 0 ;is it zero?
JR Z,A61623 ;YES, it's empty, so skip to next FCB
EX DE,HL ;NO, so DE=address of FCB
POP HL ;restore HL=filename string address
PUSH HL ;save filename string address
PUSH DE ;save FCB address
LD BC,12 ;length of filename
A61612:
LD A,(DE) ;get character from FCB
CP 3 ;is is hex 03? (end)
JR Z,A61636 ;YES, but does string also end?
CPI ;NO, so does it match the string at HL?
INC DE ;point to next character in FCB
JR Z,A61612 ;YES, so keep comparing
POP HL ;NO, mismatch, so look at next FCB
;HL=FCB address (saved as DE)
A61623:
LD A,(FILE_NUMBR) ;A=file number
CP 2 ;is it 2?
JR NZ,A61585 ;NO, so look at next FCB
A61630:
POP HL ;YES, so no more FCBs (get HL off stack)
LD A,5 ;A=5 (FILE NOT OPEN error)
OR A ;clear ZF for error exit
JR A61653
;***************************************************************************
A61636:
LD A,(HL) ;get character from filename string
CP 3 ;is it hex 03? (end)
POP HL ;HL=FCB address (saved as DE)
JR NZ,A61630 ;NO, so mismatch error exit
LD A,(IX+24) ;YES, so A=I/O mode byte from FCB
AND 7 ;mask out upper 5 bits
LD B,A ;save it in B
XOR A ;A=0, ZF=1 for OK exit
LD A,(FILE_NUMBR) ;A=file number
POP DE
A61653:
POP DE
POP IX
RET
;***************************************************************************
;EOS Function 67: CHECK FILE I/O MODE.
; On entry, IX=address of FCB, HL=address of directory entry. On exit,
; IX and HL have their entry values. This routine determines if the
; attribute of the file will permit the I/O type requested. If the mode
; check was OK, ZF=1 and A=0. Otherwise, ZF=0 and A=error code (17=BAD
; I/O MODE, 20=FILE ACCESS DENIED).
__MODE_CHECK:
PUSH IY
PUSH HL ;save dir entry address
LD A,(IX+24) ;A=I/O mode byte
AND 7 ;mask out upper 5 bits
CP 0 ;is it zero?
JR Z,A61700 ;YES, so exit error 17 (BAD I/O MODE)
CP 5 ;NO, but is less than 5?
JR NC,A61700 ;NO, so exit error 17 (BAD I/O MODE)
LD HL,A61707-1 ;YES (4,3,2,1) so point HL at mask table
ADD A,L ;offset into table
LD L,A ;HL=A61706+A
JR NC,A61681 ;no carry from add, so skip ahead
INC H ;carry, so add it to hibyte (not as stupid
;as it might seem, since you don't know
;the real address as you write the code,
;only after you assemble it
A61681:
POP IY ;IY=dir entry address (saved as HL)
PUSH IY ;save it again
LD A,(IY+12) ;A=file attribute from dir entry
AND (HL) ;mask out all but appropriate bit (if set)
JR NZ,A61696 ;bit set, so error 20 (FILE ACCESS DENIED)
POP HL ;bit was clear; get back HL=dir entry addr
POP IY
XOR A ;A=0, ZF=1 for OK exit
RET
;***************************************************************************
A61696:
LD A,20 ;A=20 (FILE ACCESS DENIED error)
JR A61702
A61700:
LD A,17 ;A=17 (BAD I/O MODE error)
A61702:
POP HL
POP IY
OR A ;ZF=0 for error exit
RET
;***************************************************************************
;TABLE OF FILE ATTRIBUTE BIT MASKS.
A61707:
DB 20H ;0010 0000 ;read protected
DB 40H ;0100 0000 ;write protected
DB 80H ;1000 0000 ;delete protected
DB 02H ;0000 0010 ;execute protected
;***************************************************************************
;EOS Function 58: RENAME FILE.
; On entry, A=device number, DE=address of old filename string, HL=address
; of new filename string. On exit, if the rename was successful, ZF=1 and
; A=0. Otherwise, ZF=0 and A=error code.
__RENAME_FILE:
PUSH BC
PUSH AF
PUSH DE
PUSH HL
EX DE,HL ;swap new filename address into DE
LD HL,QUERY_BUFFER ;HL=query buffer address
CALL __QUERY_FILE ;*Fn52: FIND FILE (WITH TYPE)
JR Z,A61768 ;found! we can't use this name, so exit
POP HL ;not found, so we'll use it
POP DE ;DE restored as old filename address
POP AF
PUSH AF
PUSH DE
PUSH HL
LD HL,QUERY_BUFFER ;HL=query buffer address
CALL __QUERY_FILE ;*Fn52: FIND FILE (WITH TYPE)
JR NZ,A61768 ;old file not found, so error exit
LD DE,QUERY_BUFFER ;found, so DE=query buffer address
POP HL ;HL=address of new filename string
PUSH HL
LD BC,12 ;length of name=12
LDIR ;copy new name to old entry in query buffr
POP HL
POP DE
POP AF
PUSH AF
PUSH DE
PUSH HL
LD HL,QUERY_BUFFER ;HL=query buffer address
CALL __SET_FILE ;*Fn53: UPDATE DIRECTORY ENTRY
JR NZ,A61768 ;update failed, so error exit ZF=0
XOR A ;update OK, so A=0, ZF=1 for OK exit
POP HL
POP DE
POP BC
POP BC
RET
;***************************************************************************
A61768:
OR A ;clear ZF for error exit
POP HL
POP DE
POP BC
POP BC
RET
;***************************************************************************
;EOS Function 59: DELETE FILE.
; On entry, A=device number, HL=address of filename string for file to
; delete. On exit, if the delete was successful, ZF=1 and A=0. Otherwise,
; ZF=0 and A=error code.
__DELETE_FILE:
PUSH DE
PUSH HL
PUSH AF
EX DE,HL ;swap filename string into DE
LD HL,QUERY_BUFFER ;HL=query buffer address
CALL __QUERY_FILE ;*Fn52: FIND FILE (WITH TYPE)
JR NZ,A61815 ;file not found, so error exit ZF=0
LD A,(QUERY_BUFFER+12) ;A=attribute byte (12) of file in buffer
BIT 7,A ;is bit 7 set? (locked)
JR NZ,A61812 ;YES, so exit error 16
OR 4 ;NO, so set bit 3 (deleted)
LD (QUERY_BUFFER+12),A ;set new attribute back into query buffer
POP AF ;restore A=device #
POP DE ;restore DE=address of filename string
PUSH DE
PUSH AF
CALL __SET_FILE ;*Fn53: UPDATE DIRECTORY ENTRY
JR NZ,A61815 ;update failed, so error exit ZF=0
XOR A ;update OK, so A=0, ZF=1
POP HL ;get AF off stack without altering flags
POP HL
POP DE
RET
;***************************************************************************
A61812:
LD A,16 ;A=16 (FILE LOCKED error)
OR A ;ZF=0 for error exit
A61815:
POP HL ;get AF off stack without altering flags
POP HL
POP DE
RET
;***************************************************************************
;EOS Function 65: READ BLOCK.
; On entry, A=device number, BCDE=block to read (BC=hiword, DE=loword), HL=
; data transfer address (DTA). On exit, if the read was successful, ZF=1.
; If not, ZF=0 and A=22 (I/O ERROR) or other error code. Oddly, this
; routine reads the block, checks the device status, then rereads the
; block. This cannot be for data integrity, as no verify operation is per-
; formed on the data (e.g. load same block into 2 places and compare them).
; The extra reading time is not noticeable from disk drives, but probably
; is significant for the tape drives. Why the routine does this I don't
; know. By comparison, Fn66 (write block) writes the block once, followed
; by a status check.
; Note added 9508.08: According to Chris Braymen, who has done extensive
; investigation into the workings of ADAMnet at the 6801 level, the double
; read is actually necessary (for technical reasons which I don't know).
; I can say that, at ADAMcon 05, while playing with his ADAMnet RAMdisk,
; I NOPed out the CALL for the second read, and no data was transferred.
; Restoring the CALL, it worked.
__READ_BLOCK:
PUSH BC
PUSH DE
PUSH HL
PUSH IX
PUSH IY
PUSH AF
LD A,2 ;number of times to retry the read
LD (RETRY_COUNT),A ;save retry count
A61832:
POP AF ;restore A=device #
PUSH AF ;save it again
CALL __RD_1_BLK ;*Fn19: READ 1 BLOCK
JR Z,A61853 ;read OK (A=0), so continue, check status
CP 155 ;read failed, but was error 155?
JR Z,A61853 ;YES, so continue, check status
PUSH HL ;NO, so enter retry loop; save DTA
LD HL,RETRY_COUNT ;HL=address of retry count
DEC (HL) ;one less chance to count
POP HL ;restore HL=DTA
JR Z,A61914 ;no more retry chances, so error 22 exit
JR A61832 ;still some chances, so keep trying
;***************************************************************************
A61853:
LD B,2 ;count for status request retries
A61855:
POP AF ;restore A=device #
PUSH AF ;save it
CALL __REQUEST_STATUS ;*Fn26: REQUEST DEVICE (in A) STATUS
JR Z,A61870 ;status 0 (done OK), so ...
CP 155 ;not zero, but is it 155? (busy but OK)
JR Z,A61855 ;YES, so keep waiting 'til it's done
DJNZ A61855 ;NO, so error; decrement retry count
JR A61914 ;no more status retries, so error 22 exit
;***************************************************************************
A61870:
POP AF ;restore A=device #
PUSH AF ;save it again
CALL A61989 ;GET NODE TYPE DATA OF DEVICE (in A) subrt
CP 0 ;is it zero?
JR NZ,A61914 ;NO, so error 22 exit
LD A,2 ;YES, so retry count=2
LD (RETRY_COUNT),A ;save retry count
POP AF ;restore A=device #
POP IY
POP IX
POP HL ;restore HL=DTA
POP DE
POP BC ;restore BCDE=block to read
PUSH BC
A61893:
PUSH AF
CALL __RD_1_BLK ;*Fn19: READ 1 BLOCK
JR Z,A61911 ;success! so exit OK
PUSH HL ;read failed, so enter retry loop
LD HL,RETRY_COUNT ;HL=address of retry count
DEC (HL) ;one less chance to read...
POP HL
JR Z,A61910 ;this was our last chance, so exit
;error code in A returned by Fn19
POP AF
JR A61893 ;let's try it again
;***************************************************************************
A61910:
OR A ;clear ZF for error exit
A61911:
POP BC ;get AF off stack without altering flags
POP BC
RET
;***************************************************************************
A61914:
POP IY
POP IY
POP IX
POP HL
POP DE
POP BC
LD A,22 ;A=22 (I/O ERROR)
RET
;***************************************************************************
;EOS Function 66: WRITE BLOCK.
; On entry, A=device number, BCDE=block to write (BC=hiword, DE=loword),
; HL=data transfer address (DTA). On exit, if the write was successful,
; ZF=1. If not, ZF=0 and A=22 (I/O ERROR). Unlike Fn65 (read block),
; this routine only writes the block once.
__WRITE_BLOCK:
PUSH BC
PUSH IY
PUSH AF ;save device #
LD A,2 ;retry count=2
LD (RETRY_COUNT),A ;save retry count
A61935:
POP AF ;restore device #
PUSH AF ;save it again
CALL __WR_1_BLOCK ;*Fn44: WRITE 1 BLOCK
JR Z,A61956 ;write OK (A=0), so continue, check status
CP 155 ;write failed but was error 155?
JR Z,A61956 ;YES, so continue, check status
PUSH HL ;NO, so entry retry loop
LD HL,RETRY_COUNT ;HL=address of retry count
DEC (HL) ;one less chance to write...
POP HL
JR Z,A61982 ;this was our last chance, so exit
JR A61935 ;let's try it again
;***************************************************************************
A61956:
LD B,2 ;B=retry count for status request
A61958:
POP AF ;restore A=device #
PUSH AF ;save it again
CALL __REQUEST_STATUS ;*Fn26: REQUEST DEVICE (in A) STATUS
JR Z,A61973 ;status OK (A=0), so continue
CP 155 ;status not zero, but is it 155? (busy)
JR Z,A61958 ;YES, so keep waiting 'til it's done
DJNZ A61958 ;NO, so decrement retry count and hope...
JR A61982 ;no more retries left, so exit error 22
;***************************************************************************
A61973:
POP AF ;restore A=device #
PUSH AF ;save it again
CALL A61989 ;GET NODE TYPE DATA OF DEVICE (in A) subrt
CP 0 ;is it zero?
JR Z,A61984 ;YES, so exit OK
A61982:
LD A,22 ;A=22 (I/O ERROR), ZF already 0
A61984:
POP BC
POP IY
POP BC
RET
;***************************************************************************
;GET NODE TYPE DATA OF DEVICE (in A) subroutine.
; On entry, A=device number, IY=address of DCB. On exit, if the device
; number is greater than 15, A returns the high nibble of the node type
; byte from the DCB. If the device number is 15 or less, A returns the
; low nibble of the node type byte. This routine handles tape 1 and tape
; 2, which share the same DCB.
A61989:
SRL A ;A/2
SRL A ;A/4
SRL A ;A/8
SRL A ;A/16 (high nibble moved to low)
CP 0 ;is it zero?
LD A,(IY+20) ;A=node type byte from DCB
JR NZ,A62008 ;NO, so make A=upper nibble of node type
AND 15 ;YES, so make A=lower nibble of node type
JR A62016 ;exit (how about just RET?)
;***************************************************************************
A62008:
SRL A ;A/2
SRL A ;A/4
SRL A ;A/8
SRL A ;A/16 (high nibble moved to low)
A62016:
RET
;***************************************************************************
;EOS Function 63: TRIM FILE.
; On entry, A=device number, DE=address of filename to trim. On exit, any
; excess blocks allocated to the file (but not actually used by it) are
; deallocated. If the next directory entry is BLOCKS LEFT, the free blocks
; are allocated for use by subsequent files. Otherwise, the space is
; wasted.
__TRIM_FILE:
PUSH HL
PUSH IX
PUSH IY
PUSH DE
PUSH AF
LD HL,FCB_BUFFER ;HL=base of FCB buffer
CALL __QUERY_FILE ;*Fn52: FIND FILE (WITH TYPE)
JP NZ,A62234 ;file not found, so error exit
LD IX,FCB_BUFFER ;IX=base of FCB buffer
LD E,(IX+19)
LD D,(IX+20) ;DE=number of blocks actually used
LD L,(IX+17)
LD H,(IX+18) ;HL=allocated length of file in blocks
OR A ;clear CF
SBC HL,DE ;HL=HL-DE
LD A,H
OR L ;did HL=DE?
JP Z,A62225 ;YES, so no trim needed -- exit OK
LD (NEW_HOLE_SIZE),HL ;NO, so new hole size in blocks=HL
LD (IX+17),E
LD (IX+18),D ;make allocated length=actual used length
LD A,(FILE_COUNT) ;A=file count (# of files in directory)
LD B,39 ;B=maximum number of files
A62071:
SUB B ;have we reached the maximum?
JR Z,A62110 ;YES, we're at the last file in directory
JR NC,A62071 ;NO, not yet, so keep subtracting
LD IX,(FCB_HEAD_ADDR) ;we're past the end; IX=address of FCB0
LD E,(IX+33)
LD D,(IX+34) ;DE=address of dir entry in DTA0
PUSH DE ;save it
LD BC,26 ;26 bytes to move
PUSH BC ;save byte count
LD HL,FCB_BUFFER ;HL=base of FCB buffer
LDIR ;move dir entry from FCB buffer to DTA0
POP DE ;DE=26 (saved as BC)
POP HL ;HL=old DE=byte offset into DTA0
ADD HL,DE ;point to next directory entry
PUSH HL ;save this address...
POP IY ;and get it back in IY
BIT 0,(IY+12) ;is bit 0 of file attribute byte set?
;(not a file)
JR Z,A62202 ;NO, it's a regular file, so we can't
;add any free blocks to BLOCKS LEFT
JR A62170 ;YES, so we're at BLOCKS LEFT
;add in the freed-up blocks
;***************************************************************************
A62110:
LD A,(FILE_COUNT) ;A=file count
LD (MOD_FILE_COUNT),A ;mod file count=file count
POP AF ;restore A=device #
POP DE ;restore DE=address of filename to trim
PUSH DE ;save device # again
PUSH AF ;save filename address again
LD HL,FCB_BUFFER ;HL=base of FCB buffer
CALL __SET_FILE ;*Fn53: UPDATE DIRECTORY ENTRY
JR NZ,A62234 ;update failed, so error exit ZF=0
POP AF ;update OK, so restore device #
POP DE ;clear stack
LD DE,A62504 ;DE=address of BLOCKS LEFT in data table
PUSH DE ;save it
PUSH AF ;save device #
LD HL,FCB_BUFFER ;HL=base of FCB buffer
CALL __QUERY_FILE ;*Fn52: FIND FILE (WITH TYPE)
LD IX,(FCB_HEAD_ADDR) ;IX=address of FCB0
LD L,(IX+33)
LD H,(IX+34) ;HL=address of dir entry in DTA0
PUSH HL ;save it on stack...
POP IY ;and get it back in IY
BIT 0,(IY+12) ;is bit 0 of attrib byte set? (not a file)
JR Z,A62234 ;NO, it's a regular file, so error exit
LD HL,FILE_COUNT ;YES (BLOCKS LEFT), so HL=file count addr
LD A,(MOD_FILE_COUNT) ;A=mod file count
INC A ;one more file counted
CP (HL) ;compare file count and mod file count+1
JR NZ,A62225 ;NOT EQUAL, so exit OK
A62170:
LD L,(IY+17) ;EQUAL, so...
LD H,(IY+18) ;HL=allocated length in blocks
LD DE,(NEW_HOLE_SIZE) ;DE=new hole size in blocks
ADD HL,DE ;add this length to the new hole size
LD (IY+17),L
LD (IY+18),H ;put this new size as the allocated length
LD L,(IY+13)
LD H,(IY+14) ;HL=start block loword
OR A ;clear CF
SBC HL,DE ;deduct the hole size from the start block
LD (IY+13),L
LD (IY+14),H ;save new start block loword
A62202:
LD A,(IX+23) ;A=device #
LD HL,(FCB_DATA_ADDR) ;HL=address of DTA0
LD E,(IX+25)
LD D,(IX+26)
LD C,(IX+27)
LD B,(IX+28) ;BCDE=block number to write
CALL __WRITE_BLOCK ;*Fn66: WRITE BLOCK
JR NZ,A62234 ;write failed, so error exit ZF=0
A62225:
POP DE ;write OK
POP DE
POP IY
POP IX
POP HL
XOR A ;A=0, AF=1 for OK exit
RET
;***************************************************************************
A62234:
OR A ;clear ZF for error exit
POP DE
POP DE
POP IY
POP IX
POP HL
RET
;***************************************************************************
;EOS Function 47: INITIALIZE DIRECTORY.
; On entry, A=device number, C=number of directory blocks to initialize,
; DE=length of volume in blocks, HL=address of new volume name string. If
; the name is longer than 12 characters, it is truncated. On exit, if the
; initialization was successful, ZF=1 and A=0. Otherwise, ZF=0 and A=error
; code.
__INIT_TAPE_DIR:
PUSH IY
PUSH BC
PUSH DE
PUSH HL
LD (DEVICE_ID),A ;save device #
LD A,C ;A=number of dir blocks to init
LD (SECTORS_TO_INIT),A ;save it in RAM
PUSH HL ;save address of new volume name string
CALL A62383 ;SCRAMBLE DTA0 subroutine
LD HL,A62426 ;base of data table for directory init
LD DE,(FCB_DATA_ADDR) ;DE=address of DTA0
LD BC,104 ;length of init table
LDIR ;move init table to DTA0
POP HL ;restore address of new volume name string
LD DE,(FCB_DATA_ADDR) ;DE=base of DTA0
LD B,12 ;max length of name
A62278:
LD A,(HL) ;get character from new volume name string
CP 3 ;is it hex 03? (end)
JR Z,A62291 ;YES, so copy it to DTA0 and continue
LD (DE),A ;NO, but copy name character to DTA0 and..
INC HL ;point to next character in string
INC DE ;point to next slot in DTA0
DJNZ A62278 ;keep going 'til end or 12 copied
LD A,3 ;no end reached, so we'll truncate name
DEC DE ;back up to byte 11 in DTA0
A62291:
LD (DE),A ;put in hex 03 (end)
LD IY,(FCB_DATA_ADDR) ;IY=base of DTA0
LD A,(SECTORS_TO_INIT) ;A=number of blocks to initialize
OR 128 ;set bit 7
LD (IY+12),A ;copy it to volume entry (MAX DIR LENGTH)
POP HL
POP DE ;restore new length of volume
POP BC ;restore C=max directory length
PUSH BC
PUSH DE
PUSH HL
LD (IY+17),E
LD (IY+18),D ;set new length of volume
PUSH DE ;save it
LD DE,78 ;length of VOLUME, BOOT, DIRECTORY entries
ADD IY,DE ;point to BLOCKS LEFT entry
POP DE ;restore new length of volume
LD (IY-9),C ;DIRECTORY entry--allocated length=C
LD (IY-7),C ;DIRECTORY entry--actual length=C
INC C ;increment C
LD (IY+13),C ;BLOCKS LEFT entry--start block=after DIR
LD B,0 ;BC=C
EX DE,HL ;swap length of volume into HL
OR A ;clear CF
SBC HL,BC ;number of free blocks=total-dir
LD (IY+17),L
LD (IY+18),H ;allocate free blocks to BLOCKS LEFT
LD A,1 ;A=1
LD (SECTOR_NO),A ;block to initialize=A
CALL A62405 ;WRITE INITIALIZED DIRECTORY BLOCK subrt
JR NZ,A62377 ;write failed, so error exit ZF=0
LD A,(SECTORS_TO_INIT) ;write OK, so get # of blocks to init
LD B,A ;put it in B
DEC B ;one less block...are we done?
JR Z,A62376 ;YES, no more blocks, so exit OK ZF=1
CALL A62383 ;NO, more to go, so SCRAMBLE DTA0 subrt
A62365:
LD HL,SECTOR_NO ;HL=address of block to initialize
INC (HL) ;point to next block
CALL A62405 ;WRITE INITIALIZED DIRECTORY BLOCK subrt
JR NZ,A62377 ;write failed, so error exit ZF=0
DJNZ A62365 ;write OK, so keep initializing 'til done
A62376:
XOR A ;all blocks initialized, exit OK A=0, ZF=1
A62377:
POP HL
POP DE
POP BC
POP IY
RET
;***************************************************************************
;SCRAMBLE DTA0 subroutine.
; On entry, DTA0 may or may not contain a directory block previously read
; in from a device. Every byte but the last is moved up 1, and the first
; is zeroed out. This makes the block unintelligible as a directory entry,
; if that is what it contained on entry. A better (though slower) approach
; would have been to zero out the whole DTA.
A62383:
PUSH BC
PUSH DE
PUSH HL
LD HL,(FCB_DATA_ADDR) ;HL=address of DTA0
LD DE,(FCB_DATA_ADDR) ;DE=address of DTA0
INC DE ;point up 1
LD BC,1023 ;bytes to move
LD (HL),0 ;zero out old 1st byte
LDIR ;move 1023 bytes from (HL) up 1
POP HL
POP DE
POP BC
RET
;***************************************************************************
;WRITE INITIALIZED DIRECTORY BLOCK subroutine.
; On entry, DTA0 contains a block of data destined for a directory. This
; data is written to the appropriate block of the directory. If write was
; successful, ZF=1 and A=0. Otherwise, ZF=0 and A=error code.
A62405:
PUSH BC
LD HL,(FCB_DATA_ADDR) ;HL=address of DTA0
LD A,(SECTOR_NO) ;A=block to initialize
LD E,A ;save it as loword/lobyte
LD D,0 ;zero out BCD
LD BC,0 ;BCDE=block number to write
LD A,(DEVICE_ID) ;A=device number
CALL __WRITE_BLOCK ;*Fn66: WRITE BLOCK
POP BC
RET
;***************************************************************************
;DIRECTORY INITIALIZATION DATA.
A62426:
DB " "
DB 80H ;
A62439:
DB 55H,0AAH,00H,0FFH ;EOS directory check
DW 0,0,0
DB 0,0,0
A62452:
DB "BOOT",3," "
DB 88H ;
DW 0,0,1,1,0
DB 0,0,0
A62478:
DB "DIRECTORY",3," "
DB 0C8H ;
DW 1,0,128,1,1024
DB 0,0,0
A62504:
DB "BLOCKS LEFT",3
DB 01H ;not a file
DW 0,0,0,0,0
DB 57H,07H,11H ;EOS revision date
;***************************************************************************
;EOS Function 70: POSITION FILE.
; Not implemented in EOS-5. Used in EOS-7 to move the read/write pointer
; in a random-access file. SmartBASIC 1.0 provides its own routine to do
; this; SmartBASIC 2.0 requires the EOS-7 routine.
__POSIT_FILE:
;***************************************************************************
;EOS Function 71: EOS1.
; Not implemented in EOS-5. In EOS-7, it is a consolidated master block
; I/O routine, part of the space-saving rewrite to add a third 1024-byte
; buffer.
__EOS_1:
;***************************************************************************
;EOS Function 72: EOS2.
; Not implemented in EOS-5. In EOS-7, it is a block I/O subroutine.
__EOS_2:
;***************************************************************************
;EOS Function 73: EOS3.
; Not implemented in either EOS-5 or EOS-7. What it was supposed to do is
; anybody's guess.
__EOS_3:
;***************************************************************************
;EOS Function 74: INCORRECT EOS VERSION ERROR.
; EOS-5 leaves several of its functions unimplemented. This might not be
; true in some later, suped-up version of EOS. Programs written to utilize
; these extra functions would bomb if run under earlier, incompatible
; versions of EOS. Consequently, jump table entries for routines which
; never got off the drawing board in EOS-5 point here to return an error
; code (A=23). This allows the incompatible program to terminate nicely
; with an error message, rather than just locking up the system when the
; call to a non-existent routine sends the program counter off to never-
; never land. Under EOS-5, Fn70 (position file), Fn71 (EOS1), Fn72 (EOS2),
; Fn73 (EOS3) remain unimplemented, and are redirected here. On exit, ZF=0
; and A=23 (INCORRECT EOS VERSION error).
; Note added 9508.08: I believe that the label CV_A stands for
; "ColecoVision Alpha", possibly refering to the OS7 ROM (which contains
; a character set). The comments in the EOS6 source say
; RETURN ERROR CODES UNTIL THESE ROUTINES ARE WRITTEN
;
; 10/12/83 VSB
; 10/14/83 RPD changed __EOS_4 to __CV_A
; so I suspect that this was a debugging trap during EOS code development.
__CV_A:
LD A,23 ;A=23 (INCORRECT EOS VERSION error)
OR A ;clear ZF for error
RET
;***************************************************************************
;EOS Functions 12/13: FIND/GET DCB ADDRESS (in IY).
; On entry, A=device number. On exit, if the device exists, ZF=1, A=device
; number and IY=DCB address. Otherwise, ZF=0 and A=1 (NON-EXISTENT DEVICE
; error).
__FIND_DCB:
__GET_DCB_ADDR:
PUSH BC
PUSH DE
LD C,A ;save device number in C
LD IY,(CURRENT_PCB) ;IY=address of current PCB
LD B,(IY+3) ;B=number of valid DCBs
XOR A ;A=0
CP B ;are there any valid DCBs?
JR Z,A62573 ;NO, so error and EXIT
LD DE,4 ;YES, so skip over PCB and...
ADD IY,DE ;point to base of first DCB (64892)
LD DE,21 ;length of each DCB
LD A,C ;A=device number
AND 15 ;zero out upper 4 bits
A62559:
CP (IY+16) ;is this the right one? (IY+16 has dev#)
JR Z,A62570 ;YES, so OK exit ZF=1
ADD IY,DE ;NO, so point to next DCB
DJNZ A62559 ;keep looking until all have been searched
JR A62573 ;couldn't find the requested DCB, so EXIT
;***************************************************************************
A62570:
LD A,C ;restore A=device #
JR A62576
;***************************************************************************
A62573:
LD A,1 ;error code=1 (NON-EXISTENT DEVICE error)
OR A ;clear zero flag
A62576:
POP DE
POP BC
RET ;on OK exit, IY points to base of DCB
;***************************************************************************
;EOS Function 26: REQUEST DEVICE (in A) STATUS.
; On entry, A=device number. On exit, if the device exists, A=status
; returned by ADAMnet, with ZF=1 if A=128, ZF=0 otherwise. If the device
; does not exist, ZF=0 and A=1 (NON-EXISTENT DEVICE error).
__REQUEST_STATUS:
CALL __FIND_DCB ;*Fn12/13: FIND/GET DCB ADDRESS (in IY)
JR NZ,A62599 ;device doesn't exist, so error exit ZF=0
;how about RET NZ?
LD (IY+0),1 ;device exists, so make status request
;write 1 to status byte tells ADAMnet to
;return the status of the device
A62588:
BIT 7,(IY+0) ;is bit 7 of status byte set? (status
;request completed; read byte for status)
JR Z,A62588 ;NO, still working on request, try again
LD A,(IY+0) ;YES, so get status in A
CP 128 ;is it 128? (OK)
A62599:
RET
;***************************************************************************
;EOS Function 60: READ DEVICE (in A) NODE TYPE.
; On entry, A=device number. On exit, if the device exists, ZF=1 and A=
; node type byte. If the device doesn't exist, ZF=0 and A=1 (NON-EXISTENT
; DEVICE error).
; The node type byte contains ADAMnet status information for each device.
; Unfortunately, there are 2 physical devices mapped to each node, with the
; high nibble and low nibble of the node type byte showing the status of
; the 2 devices, respectively. Devices which share DCBs also share node
; type bytes, hence tape 1 and tape 2 are shared, but disk 1 and disk 2 are
; separate. For tape 1, disk 1 and disk 2, the low nibble contains the
; status information; for tape 2, the high nibble. Returned values of the
; nibbles decode as follows:
; 0 No Error (everything is OK)
; 1 CRC Error (block corrupt; data failed cyclic redundancy check)
; 2 Missing Block (attempt to access past physical end of medium)
; 3 Missing Media (not in drive or drive door open)
; 4 Missing Drive (not connected or not turned on)
; 5 Write-Protected (write-protect tab covered)
; 6 Drive Error (controller or seek failure)
__RD_DEV_DEP_STAT:
PUSH IY
CALL __FIND_DCB ;*Fn12/13: FIND/GET DCB ADDRESS (in IY)
JR NZ,A62611 ;device doesn't exist, so error exit ZF=0
XOR A ;device exists, so A=0 (set ZF=1)
LD A,(IY+20) ;A=device node type byte
A62611:
POP IY
RET
;***************************************************************************
;CHECK IF DEVICE (IN A) IS READY subroutine.
; On entry, A=device number, IY=DCB address. On exit, ZF=1 and A=entry
; device number if device is ready (status=0 or bit 7 of status set).
; Otherwise, ZF=0 and A=2 (DEVICE NOT READY error).
A62614:
PUSH BC
LD C,A ;save device # in C
LD A,(IY+0) ;get status byte from DCB
CP 0 ;is it zero?
JR Z,A62629 ;YES, so exit OK
BIT 7,(IY+0) ;NO, but is bit 7 set?
JR Z,A62633 ;NO, not ready, so error exit
A62629:
XOR A ;YES, we're ready; A=0, ZF=1
LD A,C ;restore device #
JR A62636 ;exit OK
;***************************************************************************
A62633:
INC A ;clears ZF (not obvious, but it does)
LD A,2 ;A=2 (DEVICE NOT READY error)
A62636:
POP BC
RET
;***************************************************************************
;CHECK IF DEVICE I/O IS DONE subroutine.
; On entry, IY=DCB address. On exit, ZF=1 and A=0 if device I/O is
; finished (status not zero). Otherwise, ZF=0 and A=3 (I/O NOT DONE
; error).
A62638:
LD A,(IY+0) ;read DCB status byte
OR A ;is it zero?
JR NZ,A62648 ;NO, so all done
LD A,3 ;YES, A=3 (I/O NOT DONE error)
OR A ;clear ZF
RET ;error exit
;***************************************************************************
A62648:
XOR A ;A=0 and ZF set
RET ;exit OK
;***************************************************************************
;EOS Function 20: READ KEYBOARD.
; On exit, if read was successful, ZF=1 and A=character typed. If not,
; ZF=0 and A=error code.
__RD_KBD:
PUSH BC
PUSH DE
CALL __START_RD_KBD ;*Fn40: START READ KEYBOARD
JR NZ,A62664 ;start failed, so error exit ZF=0
LD C,A ;start OK, so
A62658:
LD A,C
CALL __END_RD_KBD ;*Fn9: END READ KEYBOARD
JR NC,A62658 ;not done yet, so keep trying
A62664:
POP DE ;read ended, so exit OK
POP BC
RET
;***************************************************************************
;EOS Function 27: REQUEST KEYBOARD STATUS.
; On exit, A=status. ZF=1 if A=128, otherwise ZF=0.
__REQ_KBD_STAT:
LD A,1 ;device 1=keyboard
JP __REQUEST_STATUS ;*Fn26: REQUEST DEVICE (in A) STATUS
;***************************************************************************
;EOS Function 40: START READ KEYBOARD.
; On exit, if start was successful, ZF=1 and A=1 (device number). Other-
; wise, ZF=0 and A=error code (1=NON-EXISTENT DEVICE, 2=DEVICE NOT READY).
__START_RD_KBD:
PUSH BC
PUSH DE
LD A,1 ;1=keyboard
LD DE,KEYBOARD_BUFFER ;DE=address of keyboard buffer
LD BC,1 ;1 character to read
CALL __START_RD_CH_DEV ;*Fn39: START READ CHARACTER DEVICE (in A)
POP DE
POP BC
RET
;***************************************************************************
;EOS Function 9: END READ KEYBOARD.
; On exit, if end was successful, ZF=1 and A=character typed. Otherwise,
; ZF=0 and CF reflects various error conditions. CF=0 if not done reading,
; CF=1 if done but I/O error (code in A).
__END_RD_KBD:
LD A,1 ;1=keyboard
CALL __END_RD_CH_DEV ;*Fn8: END READ CHARACTER DEVICE (in A)
JR NC,A62715 ;not done reading, so error exit CF=0
JR Z,A62709 ;done! so get character and exit OK
CP 140 ;bad end, but was it 140?
JR NZ,A62706 ;NO, so error exit CF=1, ZF=0
CALL __START_RD_KBD ;YES, so *Fn40: START READ KEYBOARD
JR Z,A62714 ;start OK, so clear CF and exit
A62706:
SCF ;start failed, so CF=1
JR A62715 ;error exit ZF=0 (how about RET?)
;***************************************************************************
A62709:
LD A,(KEYBOARD_BUFFER) ;A=last character typed (from kybd buffer)
JR A62715 ;exit OK (how about RET?)
;***************************************************************************
A62714:
OR A ;adjust ZF and clear CF
A62715:
RET
;***************************************************************************
;EOS Function 18: PRINT CHARACTER (in A).
; On entry, A=character to send to the line printer. On exit, if the
; print was successful, ZF=1 and A=0. Otherwise, ZF=0 and A=error code.
__PR_CH:
PUSH BC
PUSH DE
PUSH HL
PUSH IY
LD (PRINT_BUFFER),A ;put A as 1st character in print buffer
LD A,3 ;hex 03=logical end-of-buffer
LD (PRINT_BUFFER+1),A ;terminate buffer
LD HL,PRINT_BUFFER ;HL=address of print buffer
CALL __PR_BUFF ;*Fn17: PRINT BUFFER (at HL)
POP IY
POP HL
POP DE
POP BC
RET
;***************************************************************************
;EOS Function 17: PRINT BUFFER (at HL).
; On entry, HL=address of a print buffer terminated by hex 03. The logical
; buffer may be of any length, but only 16 characters at a time may be
; printed (physical length). On exit, if printing ended successfully, ZF=1
; and A=0. Otherwise, ZF=0 and A=error code.
__PR_BUFF:
PUSH BC
PUSH DE
PUSH HL
PUSH IY
LD A,2 ;2=printer
CALL __FIND_DCB ;*Fn12/13: FIND/GET DCB ADDRESS (in IY)
JR NZ,A62808 ;device doesn't exist, so error exit ZF=0
CALL A62614 ;device exists, so CHECK IF DEVICE (IN A)
;IS READY subroutine
JR NZ,A62808 ;device not ready, so error exit ZF=0
A62758:
LD BC,0 ;device ready, so zero out char counter
LD E,L
LD D,H ;DE=buffer start address
A62763:
LD A,3 ;hex 03=logical end-of-buffer character
CP (HL) ;is it the logical end?
JR Z,A62792 ;YES, so now let's print to logical end
INC HL ;NO, so point to next character
INC C ;count next character
LD A,C ;save it in A
CP 16 ;is it 16? (physical buffer max)
JR NZ,A62763 ;NO, so keep counting
EX DE,HL ;YES, so print full buffer: HL=start
A62776:
LD A,2 ;device 2=printer
CALL __WR_CH_DEV ;*Fn45: WRITE CHARACTER DEVICE (in A)
JR Z,A62789 ;write OK, so get next 16 characters
CP 134 ;write failed; is it still printing? (134)
JR NZ,A62808 ;NO, so error exit ZF=0
JR A62776 ;YES, so keep trying 'til it's done
;***************************************************************************
A62789:
EX DE,HL ;HL=new buffer start=old buffer end
JR A62758 ;print the next 16 character in the buffer
;***************************************************************************
A62792:
EX DE,HL ;HL=buffer start, DE=last char address
XOR A ;A=0
CP C ;have we emptied the logical buffer?
JR Z,A62808 ;YES, so OK exit ZF=1, A=0
A62797:
LD A,2 ;NO, still some to print, so get device #
CALL __WR_CH_DEV ;*Fn45: WRITE CHARACTER DEVICE (in A)
JR Z,A62808 ;write OK, so exit ZF=1, A=0
CP 134 ;write failed; is it still printing? (134)
JR Z,A62797 ;YES, so keep trying 'til it's done
A62808:
POP IY ;NO, so error exit ZF=0
POP HL
POP DE
POP BC
RET
;***************************************************************************
;THIS SECTION OF CODE DUPLICATES Fn37, BUT IS NOT USED ANYWHERE IN EOS-5.
A62814
LD (PRINT_BUFFER),A ;put A as 1st character in print buffer
LD A,3 ;hex 03=logical end-of-buffer
LD (PRINT_BUFFER+1),A ;terminate buffer
LD HL,PRINT_BUFFER ;HL=address of print buffer
CALL __START_PR_BUFF ;*Fn36: START PRINT BUFFER (at HL)
RET
;***************************************************************************
;EOS Function 37: START PRINT CHARACTER (in A).
; On entry, A=character to print. On exit, if start was successful, ZF=1
; and A=0. Otherwise, ZF=0 and A=error code.
__START_PR_CH:
LD (PRINT_BUFFER),A ;put A as 1st character in print buffer
LD A,3 ;hex 03=logical end-of-buffer
LD (PRINT_BUFFER+1),A ;terminate buffer
LD HL,PRINT_BUFFER ;HL=address of print buffer
CALL __START_PR_BUFF ;*Fn36: START PRINT BUFFER (at HL)
RET
;***************************************************************************
;EOS Function 6: END PRINT CHARACTER (in A).
; On exit, if end was successful, ZF=1. If not, ZF=0 and CF reflects
; various error conditions. CF=0 if not done printing, CF=1 if done but
; I/O error (code in A).
__END_PR_CH:
CALL __END_PR_BUFF ;*Fn5: END PRINT BUFFER (at HL)
RET
;***************************************************************************
;EOS Function 36: START PRINT BUFFER (at HL).
; On entry, HL=address of a print buffer terminated by hex 03. The logical
; buffer may be of any length, but only 16 characters at a time may be
; printed (physical length). If start was OK, ZF=1 and A=0. Otherwise,
; ZF=0 and A=error code.
__START_PR_BUFF:
PUSH BC
PUSH DE
PUSH HL
PUSH IY
LD A,2 ;2=printer
CALL __FIND_DCB ;*Fn12/13: FIND/GET DCB ADDRESS (in IY)
JR NZ,A62898 ;device doesn't exist, so error exit ZF=0
CALL A62614 ;device exists, so CHECK IF DEVICE (IN A)
;IS READY subroutine
JR NZ,A62808 ;device not ready, so error exit ZF=0
LD BC,0 ;device ready, so zero out char counter
LD E,L
LD D,H ;DE=buffer start address
A62870:
LD A,3 ;hex 03=logical end-of-buffer character
CP (HL) ;is it the logical end?
JR Z,A62890 ;YES, so now let's print to logical end
INC HL ;NO, so point to next character
INC C ;count next character
LD A,C ;save it in A
CP 16 ;is it 16? (physical end)
JR NZ,A62870 ;NO, so keep counting
EX DE,HL ;YES, so print full buffer: HL=start
LD A,2 ;device 2=printer
CALL __START_WR_CH_DEV ;*Fn42: start write character dev (in A)
JR A62898 ;exit with ZF reflecting status of start
;***************************************************************************
A62890:
EX DE,HL ;HL=new buffer start=old buffer end
XOR A ;A=0
CP C ;have we emptied the logical buffer?
LD A,2 ;device 2=printer
CALL NZ,__START_WR_CH_DEV ;NO, so *Fn42: start write char dev (in A)
A62898:
POP IY ;exit with ZF reflecting start status
POP HL
POP DE
POP BC
RET
;***************************************************************************
;EOS Function 5: END PRINT BUFFER (at HL).
; On exit, if end was successful, ZF=1. If not, ZF=0 and CF reflects
; various error conditions. CF=0 if not done printing, CF=1 if done but
; I/O error (code in A).
__END_PR_BUFF:
PUSH IY
LD A,2 ;device 2=printer
CALL __FIND_DCB ;*Fn12/13: FIND/GET DCB ADDRESS (in IY)
CALL __END_WR_CH_DEV ;*Fn11: END WRITE CHARACTER DEVICE (in A)
JR NC,A62927 ;not done writing, so error exit CF=0
JR Z,A62927 ;done! so OK exit
CP 134 ;bad end; was it 134?
JR NZ,A62927 ;NO, so I/O error exit ZF=0
LD (IY+0),3 ;YES, so request write
OR A ;adjust ZF, clear CF
A62927:
POP IY
RET
;***************************************************************************
;EOS Function 28: REQUEST PRINTER STATUS.
; On exit, A=status. ZF=1 if A=128, otherwise ZF=0.
__REQ_PR_STAT:
LD A,2 ;device number 2=printer
JP __REQUEST_STATUS ;*Fn26: REQUEST DEVICE (in A) STATUS
;***************************************************************************
;EOS Function 29: REQUEST TAPE STATUS.
; On exit, A=status. ZF=1 if A=128, otherwise ZF=0.
__REQ_TAPE_STAT:
LD A,8 ;device number 8=tape 1/2
JP __REQUEST_STATUS ;*Fn26: REQUEST DEVICE (in A) STATUS
;***************************************************************************
;EOS Function 2: CONSOLE INITIALIZATION.
; On entry, B=number of columns (X) for screen, C=number of lines (Y), D=
; column of upper left corner (X-min), E=line of upper left column (Y-min),
; HL=VRAM address of name table. Note: This routine is not used by
; SmartBASIC, though its own routine is almost identical.
__CONS_INIT:
INC B ;one more to see if we've gone too far
INC C ;ditto
LD (NUM_LINES),BC ;65183=# of lines, 65184=# of columns
LD (UPPER_LEFT),DE ;65185=upper left line, 65186=column
LD (CURSOR),DE ;65189=current cursor line, 65190=column
LD (PTRN_NAME_TBL),HL ;VRAM address of name table=HL
LD A,D
LD (X_MIN),A ;X min=upper left column
ADD A,B
DEC A
LD (X_MAX),A ;X max=(X min)+number of columns-1
LD A,E
LD (Y_MIN),A ;Y min=upper left line
ADD A,C
DEC A
LD (Y_MAX),A ;Y max=(Y min)+number of lines-1
LD A,32
LD (OLDCHAR_),A ;old character=space
LD A,95 ;character=underline
CALL A63450 ;DISPLAY CHARACTER IN A ON SCREEN subrt
RET
;***************************************************************************
;EOS Function 3: DISPLAY CHARACTER OR CONTROL CHARACTER ON SCREEN.
; On entry, character to display is in A. If the character is ^\ (28),
; D=column, E=line to quickmove the cursor to. Note: This routine is
; not used by SmartBASIC, though its own routine is almost identical.
; The control character handling routines, however, are different.
__CONS_OUT:
PUSH AF
PUSH BC
PUSH HL
PUSH IX
PUSH IY
PUSH DE
LD HL,A63502 ;HL=addr of data table of control chars
LD BC,12 ;length of table=12
CPIR ;compare table with character in A
JR NZ,A63023 ;not found, so just print regular char
LD HL,A63514 ;HL=jump table addr for control char print
ADD HL,BC
ADD HL,BC ;offset into table
LD B,A ;temporarily store character in B
LD A,(HL) ;get lobyte of execute address
INC HL ;point to hibyte
LD H,(HL) ;get hibyte of execute address in H
LD L,A ;put lobyte in L
JP (HL) ;jump to execute address in HL
;***************************************************************************
;EOS Function 1: CONSOLE DISPLAY OF NON-CONTROL CHARACTER (in A).
; On entry, A=character to display. Performs line wraparound and screen
; scroll up if necessary. Note: This routine is not used by SmartBASIC,
; though its own routine is almost identical.
__CONS_DISP:
PUSH AF
PUSH BC
PUSH HL
PUSH IX
PUSH IY
PUSH DE
A63023:
CALL A63450 ;DISPLAY CHARACTER (IN A) ON SCREEN subrt
LD HL,(CURSOR) ;L=cursor line, H=cursor column
INC H ;move cursor to next column
LD A,(X_MAX) ;A=X max
CP H ;have we gone too far right?
JR NC,A63056 ;NO, so print cursor at next position
LD A,(X_MIN) ;YES: A=X min
LD H,A ;put cursor back at start column
INC L ;go to next line
LD A,(Y_MAX) ;A=Y max
CP L ;have we gone too far down?
JR NC,A63056 ;NO, so print cursor at next position
DEC L ;YES: back up to screen bottom
PUSH HL ;save cursor coordinates
CALL A63469 ;READ OLD CHARACTER AT CURRENT CURSOR X,Y
CALL A63380 ;PRINT OLD CHARACTER AND SCROLL UP 1 LINE
POP HL ;get back cursor coordinates
A63056:
LD (CURSOR),HL ;save current cursor line and column
A63059:
CALL A63472 ;READ OLD CHARACTER AT CURRENT CURSOR X,Y
A63062:
LD A,95 ;cursor character=underline
CALL A63450 ;DISPLAY CHARACTER (IN A) ON SCREEN subrt
A63067:
POP DE
POP IY
POP IX
POP HL
POP BC
POP AF
RET
;***************************************************************************
;CARRIAGE RETURN ^M (13) subroutine.
; The cursor is moved to the far left of the screen, on the same line.
A63076:
CALL A63447 ;DISPLAY OLD CHARACTER ON SCREEN subrt
LD A,(X_MIN) ;A=X min
LD (CURSOR+1),A ;cursor column=X min
LD HL,(CURSOR) ;L=cursor line, H=cursor column
JP A63059 ;READ NEXT OLD CHARACTER AND PRINT CURSOR
;***************************************************************************
;UP ARROW (160) subroutine.
; The cursor is moved up 1 line in the same column. If the cursor was
; already at the first line, nothing happens.
A63091:
LD HL,(CURSOR) ;L=cursor line, H=cursor column
LD A,(Y_MIN) ;A=Y min
CP L ;are we at the first line?
JR Z,A63067 ;YES, so EXIT without doing anything
DEC L ;NO, so back cursor up 1 line
A63101:
CALL A63447 ;DISPLAY OLD CHARACTER ON SCREEN subrt
JP A63056 ;PRINT CURSOR AT NEW X,Y (H,L)
;***************************************************************************
;LINE FEED ^J (10) OR DOWN ARROW (162) subroutine.
; The cursor is moved down 1 line in the same column. If the cursor was
; already at the last line, line feed scrolls up the screen; down arrow
; does nothing.
A63107:
LD HL,(CURSOR) ;L=cursor line, H=cursor column
LD A,(Y_MAX) ;A=Y max
CP L ;are we at the last line?
JR Z,A63120 ;YES, so scroll up 1 line if line feed
INC L ;NO, so move cursor down 1 line
JP A63101 ;PRINT OLD CHARACTER AND PRINT CURSOR
;***************************************************************************
A63120:
LD A,10
CP B ;is it line feed?
JR NZ,A63067 ;NO, so EXIT with no screen scroll
CALL A63380 ;YES, so SCROLL UP 1 LINE subroutine
LD HL,(CURSOR) ;L=cursor line, H=cursor column
JP A63059 ;READ NEXT OLD CHARACTER AND PRINT CURSOR
;***************************************************************************
;RIGHT ARROW (161) subroutine.
; The cursor is moved right one column on the same line. If the cursor
; was already at right margin, then it hops down to the next line at the
; left margin. If the cursor was on the last line, however, the screen is
; not scrolled up.
A63134:
LD HL,(CURSOR) ;L=cursor line, H=cursor column
LD A,(X_MAX) ;A=X max
CP H ;are we at the last column?
JR Z,A63147 ;YES, so hop to next line at far left
INC H ;NO, so move cursor right 1 column
JP A63101 ;PRINT OLD CHARACTER AND PRINT CURSOR
;***************************************************************************
A63147:
LD A,(Y_MAX) ;A=Y max
CP L ;are we at the last line?
JR Z,A63067 ;YES, so EXIT (can't scroll up)
INC L ;NO, so move cursor down 1 line
LD A,(X_MIN) ;A=X min
LD H,A ;cursor column=X min
JP A63101 ;PRINT OLD CHARACTER AND PRINT CURSOR
;***************************************************************************
;LEFT ARROW (163) OR BACKSPACE ^H (8) subroutine.
; The cursor is moved left one column on the same line. If the cursor was
; already at the left margin, it hops up to the previous line at the right
; margin. If the cursor was on the first line, however, the screen is not
; scrolled down.
A63161:
LD HL,(CURSOR) ;L=cursor line, H=cursor column
LD A,(X_MIN) ;A=X min
CP H ;are we at the first column?
JR Z,A63174 ;YES, so hop up 1 line at far right
DEC H ;NO, so move cursor left 1 column
JP A63101 ;PRINT OLD CHARACTER AND PRINT CURSOR
;***************************************************************************
A63174:
LD A,(Y_MIN) ;A=Y min
CP L ;are we at the first line?
JR Z,A63067 ;YES, so exit (can't scroll down)
DEC L ;NO, so move cursor up 1 line
LD A,(X_MAX) ;A=X max
LD H,A ;cursor column=X max
JP A63101 ;PRINT OLD CHARACTER AND PRINT CURSOR
;***************************************************************************
;FORM FEED ^L (12) subroutine.
; The entire screen is erased. The cursor is placed at the upper left
; corner.
A63188:
LD A,(NUM_LINES) ;A=number of lines
LD B,A ;in B for counter
LD HL,(UPPER_LEFT) ;L=line of upper left corner, H=column
CALL A63308 ;ERASE B LINES STARTING AT H,L subroutine
LD A,32
LD (OLDCHAR_),A ;old char=space; fall into next routine
;***************************************************************************
;HOME (128) subroutine.
; The cursor is placed at the upper left corner.
A63203
CALL A63447 ;DISPLAY OLD CHARACTER ON SCREEN
LD HL,(UPPER_LEFT) ;L=line of upper left corner, H=column
JP A63056 ;PRINT CURSOR AT NEW X,Y (H,L)
;***************************************************************************
;SYNCHRONOUS IDLE ^V (22) subroutine.
; The physical screen line is erased from the current cursor position to
; the end of the line. The cursor is not moved.
A63212:
CALL A63334 ;ERASE TO END OF CURRENT LINE subroutine
JP A63062 ;PRINT CURSOR
;***************************************************************************
;CANCEL ^X (24) subroutine.
; The physical screen line is erased from the current cursor position to
; the end of the line. All lines below the current line are also erased.
; The cursor is not moved.
A63218:
CALL A63334 ;ERASE TO END OF CURRENT LINE subroutine
LD HL,(CURSOR) ;L=cursor line, H=cursor column
INC L ;down to next line
LD A,(Y_MIN) ;A=Y min
LD C,A ;in C
LD A,(NUM_LINES) ;A=number of lines
ADD A,C ;A=line number of last screen line
SUB L ;A=lines left between current and bottom
JP Z,A63062 ;no lines left, so exit
LD B,A ;B=lines left (for counter)
LD A,(X_MIN) ;A=X min
LD H,A ;new cursor column at far left
CALL A63308 ;ERASE B LINES STARTING AT H,L subroutine
JP A63062 ;PRINT CURSOR
;***************************************************************************
;FILE SEPARATOR ^\ (28) subroutine.
; This routine is used to "quick move" the cursor to any position on the
; screen. On entry, D=column (X), E=line (Y) to move to. The cursor is
; moved to these coordinates if they are not out of range.
A63248:
LD A,(X_MIN) ;A=X min
CP D ;is it at the left margin?
JR Z,A63257 ;YES, so keep checking
JP NC,A63067 ;NO, too far left (right falls through)
A63257:
LD A,(X_MAX) ;A=X max
CP D ;is it at the right margin?
JR Z,A63266 ;YES, so keep checking
JP C,A63067 ;NO, too far right (left falls through)
A63266:
LD A,(Y_MIN) ;A=Y min
CP E ;is it at the top?
JR Z,A63275 ;YES, so keep checking
JP NC,A63067 ;NO, too far up (below falls through)
A63275:
LD A,(Y_MAX) ;A=Y max
CP E ;is it at the bottom?
JR Z,A63284 ;YES, so
JP C,A63067 ;NO, too far down (above falls through)
A63284:
EX DE,HL ;HL=new cursor coordinates
JP A63101 ;PRINT OLD CHARACTER AND PRINT CURSOR
;***************************************************************************
;FILL LINE BUFFER WITH SPACES STARTING AT HL subroutine.
; On entry, HL=address of position in line buffer to start filling with
; spaces, B=number of spaces to fill. It jumps into the next routine,
; bypassing the setup for erasing the entire buffer.
A63288:
PUSH HL
PUSH BC
JR A63299
;***************************************************************************
;FILL ENTIRE LINE BUFFER WITH SPACES.
A63292:
PUSH HL
PUSH BC
LD HL,LINEBUFFER_ ;HL=address of line buffer
LD B,32 ;number of characters to fill in buffer
A63299:
LD A,32 ;character=space
A63301:
LD (HL),A ;put it in buffer
INC HL ;point to next slot
DJNZ A63301 ;keep filling 'til count is zero
POP BC
POP HL
RET
;***************************************************************************
;ERASE B LINES STARTING AT H,L subroutine.
; On entry, B=number of lines to erase, HL=current cursor position.
A63308:
CALL A63292 ;FILL ENTIRE LINE BUFFER WITH SPACES subrt
LD A,(NUM_COLUMNS) ;A=number of columns
LD C,A ;in C
A63315:
PUSH BC ;save it
PUSH HL ;save current cursor position (H,L)
CALL A63484 ;GET VRAM ADDR OF current CURSOR X,Y IN DE
LD B,0 ;BC=C (number of bytes to write)
LD HL,LINEBUFFER_ ;HL=address of line buffer
CALL WRITE_VRAM ;Fn78: WRITE VRAM
POP HL ;restore cursor position
INC L ;down to next line
POP BC ;restore count of lines to erase
DJNZ A63315 ;keep going until all lines are erased
RET
;***************************************************************************
;ERASE TO END OF CURRENT LINE subroutine.
A63334:
CALL A63353 ;READ REST OF LINE FROM VRAM INTO BUFFER
PUSH HL ;save line buffer start address
PUSH BC ;C=# of char read (from READ REST OF LINE)
LD B,C ;B=count of bytes to write
CALL A63288 ;FILL LINE BUFFER WITH SPACES START AT HL
POP BC ;restore C=# char replaced by with spaces
POP HL ;restore HL=line buffer start address
LD A,32
LD (OLDCHAR_),A ;old character=space
JP WRITE_VRAM ;Fn78: WRITE VRAM
;***************************************************************************
;READ REST OF CURRENT LINE FROM VRAM INTO LINE BUFFER subroutine.
; On exit, C=number of characters between the cursor and the physical end
; of the line, and the line buffer contains those characters.
A63353:
LD HL,(CURSOR) ;L=cursor line, H=cursor column
LD A,(X_MAX) ;A=X max
INC A
SUB H ;A=number of columns to fill with spaces
LD C,A ;in C
CALL A63484 ;GET VRAM ADDR OF CURRENT CURSOR X,Y IN DE
LD HL,LINEBUFFER_ ;HL=address of line buffer
LD B,0 ;BC=C (count of bytes to read)
PUSH BC
PUSH HL
PUSH DE
CALL READ_VRAM ;Fn79: READ VRAM
POP DE
POP HL
POP BC
RET
;***************************************************************************
;SCROLL UP 1 LINE subroutine.
; On exit, the cursor is positioned at the lower left corner of the screen.
A63380:
CALL A63447 ;DISPLAY OLD CHARACTER ON SCREEN subrt
LD HL,(UPPER_LEFT) ;L=line of upper left corner, H=column
PUSH HL ;save upper left corner coordinates
;this is where we start copying to
CALL A63484 ;GET VRAM ADDR OF CURRENT CURSOR X,Y IN DE
POP HL ;get back upper left corner coordinates
LD A,(NUM_LINES) ;A=number of lines
LD B,A ;in B
DEC B ;one less for count
A63396:
PUSH BC ;save count
PUSH DE ;save VRAM address
INC L ;point to next line for source
PUSH HL ;save new source cursor coordinates
;this is where we copy from
CALL A63484 ;GET VRAM ADDR OF CURRENT CURSOR X,Y IN DE
PUSH DE ;save new source VRAM address
LD A,(NUM_COLUMNS) ;A=number of columns
LD C,A ;in C
LD B,0 ;BC=C (count of columns to read)
PUSH BC ;save it
LD HL,LINEBUFFER_ ;HL=address of line buffer
CALL READ_VRAM ;Fn79: READ VRAM (current line to move)
POP BC ;restore BC=column read count
POP DE ;restore DE=source VRAM address
POP HL ;restore HL=source line coordinates
EX (SP),HL ;save this on stack
;now HL=VRAM address of target line
EX DE,HL ;DE=VRAM address of target line
;HL=source VRAM address
PUSH HL ;save source VRAM address
LD HL,LINEBUFFER_ ;HL=address of line buffer
CALL WRITE_VRAM ;Fn78: WRITE VRAM (old line moved up 1)
POP DE ;DE=source VRAM address
;this becomes target for next loop cycle
POP HL ;HL=source line coordinates
;these become target for next loop cycle
POP BC ;restore count of lines to erase
DJNZ A63396 ;decrement it and keep going 'til done
CALL A63292 ;FILL ENTIRE LINE BUFFER WITH SPACES subrt
LD HL,LINEBUFFER_ ;HL=address of line buffer
LD A,(NUM_COLUMNS) ;A=number of columns
LD C,A ;B=0, so BC=A (number of bytes to write)
JP WRITE_VRAM ;Fn78: WRITE VRAM (erase the last line)
;***************************************************************************
;DISPLAY OLD CHARACTER ON SCREEN subroutine.
; This is also used to display any character in A by entry at A63450.
A63447:
LD A,(OLDCHAR_) ;A=old character
A63450:
PUSH HL
LD HL,(CURSOR) ;L=cursor line, H=cursor column
CALL A63484 ;GET VRAM ADDR OF CURRENT CURSOR X,Y IN DE
LD HL,LINEBUFFER_ ;HL=address of line buffer
LD (HL),A ;put A into line buffer
LD BC,1 ;1 character to write
CALL WRITE_VRAM ;Fn78: WRITE VRAM
POP HL
RET
;***************************************************************************
;READ OLD CHARACTER AT CURRENT CURSOR X,Y subroutine.
; The result is stored at 65145.
A63469:
LD HL,(CURSOR) ;L=cursor line, H=cursor column
A63472:
CALL A63484 ;GET VRAM ADDR OF CURRENT CURSOR X,Y IN DE
LD BC,1 ;1 byte to read
LD HL,OLDCHAR_ ;HL=address of old character
JP READ_VRAM ;Fn79: READ VRAM
;***************************************************************************
;GET VRAM ADDRESS OF CURRENT CURSOR X,Y IN DE subroutine.
; On entry, H=current cursor column, L=current cursor line. On exit, DE=
; VRAM address of these coordinates in the name table. NOTE: To use this
; routine in 40-column text mode, it must be altered to multiply by 40
; instead of 32. (E.g. save L*8 and then add it to L*32.)
A63484:
LD E,H ;save cursor column in E
LD H,0 ;zero out L (HL=L)
ADD HL,HL ;L*2
ADD HL,HL ;L*4
ADD HL,HL ;L*8
ADD HL,HL ;L*16
ADD HL,HL ;L*32
LD D,0 ;zero out D (DE=E=saved cursor column)
ADD HL,DE ;HL=(32*cursor line)+cursor column
LD DE,(PTRN_NAME_TBL) ;DE=VRAM address of name table
ADD HL,DE ;HL=offset address
EX DE,HL ;put it in DE
RET
;***************************************************************************
;DATA TABLE OF CONTROL CHARACTERS FOR SCREEN PRINTING.
A63502:
DB 08H ;backspace ^H (8)
DB 0DH ;carriage return ^M (13)
DB 0AH ;line feed ^J (10)
DB 0CH ;form feed ^L (12)
DB 80H ;home (128)
DB 16H ;synchronous idle ^V (22)
DB 18H ;cancel ^X (24)
DB 1CH ;file separator ^\ (28)
DB 0A0H ;up arrow (160)
DB 0A2H ;down arrow (162)
DB 0A3H ;left arrow (163)
DB 0A1H ;right arrow (161)
;***************************************************************************
;VECTOR TABLE FOR PROCESSING CONTROL CHARACTERS.
; *** Note that the vectors are in reverse order from the above table!!
A63514:
DW A63134 ;right arrow (161)
DW A63161 ;left arrow (163)
DW A63107 ;down arrow (162)
DW A63091 ;up arrow (160)
DW A63248 ;file separator ^\ (28)
DW A63218 ;cancel ^X (24)
DW A63212 ;synchronous idle ^V (22)
DW A63203 ;home (128)
DW A63188 ;form feed ^L (12)
DW A63107 ;line feed ^J (10)
DW A63076 ;carriage return ^M (13)
DW A63161 ;backspace ^H (8)
;***************************************************************************
;EOS Function 0: EOS START/INITIALIZATION.
; This routine is jumped to by a powerup boot program located in page 0 of
; the SmartWriter ROM. (When the Z80A CPU is reset, the program counter is
; forced to 0000, and execution begins there. ADAM is hardwired so that
; this accesses the SmartWriter ROM.) On entry, 8K of EOS have already
; been copied from the EOS ROM to the upper 32K of RAM (starting at 57344).
; The EOS ROM is still switched in as the lower 32K. Function 0 sets up
; the EOS stack, moves up the EOS data tables by 1 byte (why I don't know),
; sets the revision number byte to 5, gets the I/O ports from OS-7, turns
; off the sound, fills all 16K of VRAM with zeroes, then bank switches the
; lower 32K to RAM. After further setup, it tries to load a bootstrap
; program from either disk (1, then 2) or tape (1, then 2). If a boot is
; successfully loaded, the routine jumps to it at 51200; otherwise, the
; routine jumps to Function 61 (go to SmartWriter). Note that tape 1 and
; tape 2 share the same DCB, and the node type byte of the tape DCB shows
; whether tape 2 is available (hex 03) or not (hex 33).
__EOS_START:
LD SP,EOS_STACK ;SP points to top of EOS stack
LD BC,327 ;number of bytes to move with LDIR
LD DE,CLEAR_RAM_START+1 ;new start of EOS RAM table
LD HL,CLEAR_RAM_START ;old start of EOS RAM table
XOR A
LD (HL),A ;zero out old start
LDIR ;move old EOS RAM table to 64865-65192
;this effectively scrambles any old EOS
;version which might have been in RAM
LD A,5
LD (REV_NUM),A ;set EOS revision=5
CALL PORT_COLLECTION ;Fn75: GET I/O PORTS FROM OS-7
CALL TURN_OFF_SOUND ;Fn97: SOUND OFF
LD A,0 ;character to fill VRAM=0
LD HL,0 ;fill from start of VRAM
LD DE,16384 ;16K to fill
CALL FILL_VRAM ;Fn82: FILL VRAM WITH 1 CHARACTER (in A)
LD A,(MEM_CNFG01) ;A=memory configuration 1
;lower 32K RAM, upper 32K RAM
CALL SWITCH_MEM ;Fn76: BANK SWITCH MEMORY (to A)
CALL __HARD_INIT ;*Fn15: HARD INITIALIZATION (COLD BOOT)
LD DE,THREE1K_BLKS ;DE=start of DTA block (dir, file1, file2)
LD HL,FCB_S ;HL=start of FCB block (0, 1, 2)
CALL __FMGR_INIT ;*Fn46: INITIALIZE FILE MANAGER
LD A,8 ;
LD (CURRENT_DEV),A ;current device=tape 1
LD A,4 ;but let's try disk 1 anyway...
CALL __REQUEST_STATUS ;*Fn26: REQUEST DEVICE (in A) STATUS
JR NZ,A63622 ;disk 1 doesn't exist, so try disk 2
LD A,4 ;disk 1 available!
CALL __FIND_DCB ;*Fn12/13: FIND/GET DCB ADDRESS (in IY)
LD A,(IY+20) ;A=node type from DCB
AND 15 ;zero out upper 4 bits (get lower nibble)
CP 3 ;is it 3?
LD A,4 ;restore device number
JR C,A63688 ;less than 3 (OK), so let's read disk 1
A63622:
LD A,5 ;3 or greater (error), so try disk 2
CALL __REQUEST_STATUS ;*Fn26: REQUEST DEVICE (in A) STATUS
JR NZ,A63645 ;disk 2 doesn't exist either; try tape 1
LD A,5 ;disk 2 available!
CALL __FIND_DCB ;*Fn12/13: FIND/GET DCB ADDRESS (in IY)
LD A,(IY+20) ;A=node type from DCB
AND 15 ;zero out upper 4 bits (get lower nibble)
CP 3 ;is it 3?
LD A,5 ;restore device number
JR C,A63688 ;less than 3 (OK), so let's read disk 2
A63645:
LD A,8 ;3 or greater (error), so let's try tapes
CALL __REQUEST_STATUS ;*Fn26: REQUEST DEVICE (in A) STATUS
JR NZ,A63682 ;no tape drives exist!!
;we have no recourse but SmartWriter...
LD A,8 ;tapes available!
CALL __FIND_DCB ;*Fn12/13: FIND/GET DCB ADDRESS (in IY)
LD A,(IY+20) ;A=node type from DCB
PUSH AF ;save it
AND 15 ;zero out upper 4 bits (get lower nibble)
CP 3 ;is it 3?
JR C,A63685 ;less than 3 (OK), so let's read tape 1
POP AF ;3 or greater (error), so let's try tape 2
;restore node type byte
SRL A ;A/2
SRL A ;A/4
SRL A ;A/8
SRL A ;A/16 -- get high nibble
CP 3 ;is it 3?
LD A,24 ;device=tape 2
JR C,A63688 ;less than 3 (OK), so let's read tape 2
A63682:
JP _GOTO_WP ;3 or greater (error), so
;Fn61: GO TO SmartWriter
;***************************************************************************
A63685:
POP AF ;clear stack (if necessary)
LD A,8 ;device 8=tape 1
A63688:
LD (CURRENT_DEV),A ;current dev=A (enter here for other devs)
A63691:
LD HL,COLD_START_ADDR ;HL=data transfer area for boot program
LD BC,0 ;hiword of block to read=0
LD A,(CURRENT_DEV) ;A=current device
LD DE,0 ;loword of block to read=0
CALL __RD_1_BLK ;*Fn19: READ 1 BLOCK
JP Z,A63727 ;read OK, so run the boot program at 51200
LD C,A ;read failed, so save error code in C
LD A,(CURRENT_DEV) ;A=current device
AND 15 ;zero out upper 4 bits of device number
CP 8 ;is it a tape? (08h=tape 1, 18h=tape 2)
JR NZ,A63691 ;NO, so try the read again (infinite loop)
LD A,C ;YES, so get error code from C
CP 155 ;is it 155?
JR Z,A63691 ;YES, so try the read again
JP A63682 ;NO, so roundabout jump to SmartWriter
;why not just JP _GOTO_WP?
;***************************************************************************
A63727:
LD A,(CURRENT_DEV) ;A=current device
LD B,A ;B=current device
JP COLD_START_ADDR ;jump to block 0 program read in at 51200
;***************************************************************************
;EOS Function 15: HARD INITIALIZATION (COLD BOOT).
; Current PCB address is set to 65216. After a hard reset of ADAMnet, the
; old PCB, DCBs, and RESERVED_BYTE are zeroed out (addresses 65216-65535).
; The Z80A and master 6801 clocks are synchronized, and ADAMnet is scanned
; for devices, creating new DCBs as active devices are found.
__HARD_INIT:
PUSH BC
PUSH DE
PUSH HL
PUSH IY
LD HL,PCB ;HL=address of PCB
LD (CURRENT_PCB),HL ;save it in RAM
CALL __HARD_RESET_NET ;*Fn16: HARD RESET ADAMnet
CALL __DLY_AFT_HRD_RES ;*Fn4: DELAY AFTER HARD RESET
LD HL,PCB ;HL=current PCB address
LD DE,PCB+1 ;DE=new PCB address
LD BC,319 ;count of bytes to wipe
LD (HL),0 ;zero out old PCB status byte
LDIR ;wipe RAM by sequential copy of zeroes
A63764:
CALL __SYNC ;*Fn43: SYNCH Z80A & MASTER 6801 CLOCKS
JR NZ,A63764 ;synch failed, but keep trying
CALL __SCAN_ACTIVE ;*Fn30: SCAN ADAMnet FOR DEVICES
POP IY
POP HL
POP DE
POP BC
RET
;***************************************************************************
;EOS Function 31: SOFT INITIALIZATION (WARM BOOT).
; On entry, HL=new PCB address. This routine is like Function 15 (cold
; boot) with two exceptions. First, the new PCB address is supplied on
; entry, not automatically set to 65216. Second, it zeroes out the PCB
; and the DCBs, but *not* the RESERVED_BYTE (65535). The significance of
; this latter difference is not clear.
__SOFT_INIT:
PUSH BC
PUSH DE
PUSH HL
PUSH IY
LD (CURRENT_PCB),HL ;save new PCB address=HL
CALL __HARD_RESET_NET ;*Fn16: HARD RESET ADAMnet
CALL __DLY_AFT_HRD_RES ;*Fn4: DELAY AFTER HARD RESET
LD HL,(CURRENT_PCB) ;HL=current PCB address
LD E,L
LD D,H ;DE=HL=current PCB address
INC DE ;new PCB address=1 above old
LD BC,318 ;count of bytes to move
LD (HL),0 ;zero out first byte of old PCB
LDIR ;wipe RAM by sequential copy of zeroes
A63805:
CALL __SYNC ;*Fn43: SYNCH Z80A & MASTER 6801 CLOCKS
JR NZ,A63805 ;synch failed, but keep trying
CALL __SCAN_ACTIVE ;*Fn30: SCAN ADAMnet FOR DEVICES
POP IY
POP HL
POP DE
POP BC
RET
;***************************************************************************
;EOS Function 16: HARD RESET ADAMnet.
; Sends 15 out the reset port (63), waits a bit, then sends 0.
__HARD_RESET_NET:
LD A,(NET_RESET_PORT) ;A=ADAMnet reset port (63)
LD C,A ;save it in C
LD A,15
OUT (C),A ;send 15 out port 63
LD A,0 ;A=0
A63829:
NOP
NOP
NOP ;delay a bit
DEC A ;A=255,254,253,...
JR NZ,A63829 ;wait for 256 idle loop cycles
XOR A ;A=0
OUT (C),A ;send 0 out port 63
RET
;***************************************************************************
;EOS Function 4: DELAY AFTER HARD RESET.
; This do-nothing loop seem unnecessarily complicated, but its complexity
; is probably due to its history. The necessary delay time was probably
; determined empirically, and this programming structure allows loops of
; varying lengths to be constructed simply by changing B and DE.
__DLY_AFT_HRD_RES:
PUSH BC
PUSH DE
LD B,1 ;1 time through big loop
A63843:
LD DE,1 ;DE=1 (make bigger for longer delay)
A63846:
DEC DE ;DE=0 (decrement counter)
LD A,D
OR E ;is DE=0?
JR NZ,A63846 ;NO, so keep idling
DJNZ A63843 ;YES, little loop done; is big loop done?
POP DE ;NO, so exit
POP BC
RET
;***************************************************************************
;EOS Function 43: SYNCHRONIZE Z80A and MASTER 6801 CLOCKS.
; On exit, ZF=1 and A=0 if synch was OK. If synch failed, ZF=0 and A=18
; (SYNCH1 FAILED) or A=19 (SYNCH2 FAILED).
__SYNC:
PUSH IY
PUSH HL
PUSH BC
PUSH DE
LD IY,(CURRENT_PCB) ;IY=address of current PCB
LD (IY+3),0 ;zero out device # byte of PCB
LD (IY+0),1 ;request status of Z80A
LD DE,0 ;DE=0
LD B,2 ;number of times through status read loop
A63878:
DEC DE ;DE=255,254,253,...
LD A,D
OR E ;is DE=0?
JR NZ,A63893 ;NO, so read status
LD DE,0 ;YES, so why reset it?
DJNZ A63893 ;go through the read loop again
LD A,18 ;A=18 (SYNCH1 FAILED error)
OR A ;clear ZF
JR A63941 ;exit error
;***************************************************************************
A63893:
LD A,(IY+0) ;read processor status byte
CP 129 ;is it 129? (in synch)
JR NZ,A63878 ;NO, so keep polling until it is
LD (IY+0),2 ;YES, so request master 6801 status
LD DE,0 ;DE=0
LD B,2 ;number of times through status read loop
A63909:
DEC DE ;DE=255,254,253,...
LD A,D
OR E ;is DE=0?
JR NZ,A63924 ;NO, so read status
LD DE,0 ;YES, so why reset it?
DJNZ A63924 ;go through the read loop again
LD A,19 ;A=19 (SYNCH2 FAILED error)
OR A ;clear ZF
JR A63941 ;exit error
;***************************************************************************
A63924:
LD A,(IY+0) ;read processor status byte
CP 130 ;is it 130? (in synch)
JR NZ,A63909 ;NO, so keep polling until it is
PUSH IY ;save PCB address...
POP HL ;in HL
LD (IY+1),L
LD (IY+2),H ;save PCB address in PCB
XOR A ;A=0, ZF=1 for OK exit
A63941:
POP DE
POP BC
POP HL
POP IY
RET
;***************************************************************************
;EOS Function 30: SCAN ADAMnet FOR DEVICES.
; All DCBs are zeroed. The device count in the PCB is zeroed. ADAMnet
; is scanned for devices 1-15. If the device is active (status=128), a
; new 21-byte DCB is allocated, and the PCB device count is incremented.
; Otherwise, scanning continues. On exit, PCB byte 3 has the number of
; active devices, and the DCBs follow consecutively. Note: tape 1
; (device 8) and tape 2 (device 24) share the same DCB.
__SCAN_ACTIVE:
PUSH BC
PUSH DE
PUSH HL
PUSH IY
PUSH IX
LD HL,(CURRENT_PCB) ;HL=address of current PCB
LD DE,4
ADD HL,DE ;offset to start of DCB space
LD E,L
LD D,H ;DE=DCB start address...
INC DE ;plus 1
LD BC,314 ;count of bytes to wipe
LD (HL),0 ;zero out old first byte
LDIR ;wipe RAM by sequential copy of zeroes
LD IY,(CURRENT_PCB) ;IY=address of current PCB
LD DE,4
ADD IY,DE ;IY=DCB start address
LD IX,(CURRENT_PCB) ;IX=address of current PCB
LD (IX+3),1 ;add first device
LD A,1 ;device #1=keyboard
A63990:
PUSH AF ;save it
LD (IY+16),A ;byte 16 is device #
LD (IY+0),1 ;request status of device
A63998:
BIT 7,(IY+0) ;is bit 7 set? (new status ready to read?)
JR Z,A63998 ;NO, so keep waiting for status
LD A,(IY+0) ;YES, so get status in A
CP 128 ;is it 128?
JR Z,A64022 ;YES, so add it to active list
POP AF ;NO, restore device #
INC A ;try next device
CP 16 ;all done? (max 15)
JR NZ,A63990 ;NO, so keep looking
DEC (IX+3) ;YES, so adjust device count
JR A64039 ;exit
;***************************************************************************
A64022:
INC (IX+3) ;one more active device
LD DE,21 ;length of DCB=21
ADD IY,DE ;offset to start of next DCB
POP AF ;restore device #
INC A ;try next device
CP 16 ;all done? (max=15)
JR NZ,A63990 ;NO, so keep looking
DEC (IX+3) ;YES, so adjust device count
A64039:
POP IX
POP IY
POP HL
POP DE
POP BC
RET
;***************************************************************************
;EOS Function 25: RELOCATE PCB (to HL).
; On entry, HL=new address of PCB.
__RELOC_PCB:
PUSH IY
LD IY,(CURRENT_PCB) ;IY=address of current PCB
LD (IY+1),L
LD (IY+2),H ;save new PCB address in old PCB
LD (IY+0),3 ;request relocation
A64063:
LD A,(IY+0) ;read status
CP 131 ;is it 131? (relocate finished)
JR NZ,A64063 ;NO, so keep reading
LD (CURRENT_PCB),HL ;YES, so save new PCB address
POP IY
RET
;***************************************************************************
;EOS Function 14: GET PCB ADDRESS (in IY).
; On exit, IY=current PCB address.
__GET_PCB_ADDR:
LD IY,(CURRENT_PCB) ;IY=address of current PCB
RET
;***************************************************************************
;EOS Function 33: SOFT RESET KEYBOARD.
__SOFT_RES_KBD:
LD A,1 ;1=keyboard
JR __SOFT_RES_DEV ;*Fn32: SOFT RESET DEVICE (in A)
;***************************************************************************
;EOS Function 34: SOFT RESET PRINTER.
__SOFT_RES_PR:
LD A,2 ;2=printer
JR __SOFT_RES_DEV ;*Fn32: SOFT RESET DEVICE (in A)
;***************************************************************************
;EOS Function 35: SOFT RESET TAPE.
__SOFT_RES_TAPE:
LD A,8 ;8=tape 1/2
JR __SOFT_RES_DEV ;*Fn32: SOFT RESET DEVICE (in A)
;omit this!
;***************************************************************************
;EOS Function 32: SOFT RESET DEVICE (in A).
; On entry, A=device number. On exit, A=128 and ZF=1 if device is reset
; and ready for use. ZF=0 if device doesn't exist or is busy.
__SOFT_RES_DEV:
PUSH IY
CALL __FIND_DCB ;*Fn12/13: FIND/GET DCB ADDRESS (in IY)
JR NZ,A64120 ;device doesn't exist; error exit
CALL A62614 ;CHECK IF DEVICE (IN A) IS READY subrt
JR NZ,A64120 ;in use, so error exit
LD (IY+0),2 ;request reset of device (IY=DCB address)
A64109:
BIT 7,(IY+0) ;is status code ready to read yet?
JR Z,A64109 ;NO (bit 7 clear) so keep waiting
LD A,(IY+0) ;YES, so get status in A
CP 128 ;it 128? (reset and ready for use)
A64120:
POP IY
RET
;***************************************************************************
;EOS Function 21: READ KEYBOARD STATUS BYTE.
__RD_KBD_RET_CODE:
LD A,1 ;1=keyboard
JR __RD_RET_CODE ;*Fn23: READ DEVICE (in A) STATUS BYTE
;***************************************************************************
;EOS Function 22: READ PRINTER STATUS BYTE.
__RD_PR_RET_CODE:
LD A,2 ;2=printer
JR __RD_RET_CODE ;*Fn23: READ DEVICE (in A) STATUS BYTE
;***************************************************************************
;EOS Function 24: READ TAPE STATUS BYTE.
__RD_TAPE_RET_CODE:
LD A,8 ;8=tape 1/2
JR __RD_RET_CODE ;*Fn23: READ DEVICE (in A) STATUS BYTE
;omit this!
;***************************************************************************
;EOS Function 23: READ DEVICE (in A) STATUS BYTE.
; On entry, A=device number. On exit, if the device does not exist, ZF=0.
; Otherwise, ZF=1 and A=status byte from DCB.
__RD_RET_CODE:
PUSH IY
CALL __FIND_DCB ;*Fn12/13: FIND/GET DCB ADDRESS (in IY)
JR NZ,A64145 ;device dosn't exist; error exit
LD A,(IY+0) ;A=status byte
A64145:
POP IY
RET
;***************************************************************************
;EOS Function 61: GO TO SmartWriter.
; Bank switches to the SmartWriter ROM, then jumps to the first byte of
; code at address 256.
__GOTO_WP:
LD A,(MEM_CNFG00) ;A=memory configuration 0
;lower 32K=SmartWriter ROM, upper 32K=RAM
CALL SWITCH_MEM ;Fn76: BANK SWITCH MEMORY (to A)
JP 256 ;jump to first byte of SmartWriter code
;***************************************************************************
;EOS Function 62: READ EOS.
; Not implemented in EOS-5. Presumably this routine was intended to read
; in a fresh copy of the current operating system from the EOS ROM (which
; can hold up to 4 different 8K EOS versions). A user program not needing
; EOS routines could use the space for itself, then restore EOS when done.
; Perhaps.
__READ_EOS:
RET
; Note added 9508.08: the EOS6 source has the following interesting note,
; followed by a block of commented-out code. Here are both note and code
; in their entirety:
; CODE FROM HERE TO END OF EOS_UTIL WAS INSERTED AFTER THE
; REV. 06 ROM WAS BURNED. IT HAS BEEN COMMENTED OUT TO MAINTAIN
; COMPATIBILITY WITH THAT ROM.
;
; END
;
;EOS_CODE_START EQU 00800H
;EOS_SIZE EQU 02000H-800H
;EOS_DEST EQU 0E000H+800H
;
; LD A,0 ;BANK SWITCH IN THE BOOT ROMS
; OUT (7FH),A ; (I HOPE)
;
; LD HL,EOS_CODE_START ;WHERE DOES EOS SIT IN BOOT ROMS
; LD DE,EOS_DEST ;WHERE EOS GOES
; LD BC,EOS_SIZE ;HOW BIG EOS IS
; LDIR ;MOVE EOS INTO PLACE
;
; LD A,3 ;BANK SWITCH IN OS_7
; OUT (7FH),A ; (I KNOW)
;
; RET
; The amazing things about this code are that
; (1) it doesn't copy all of EOS (it skips the first 2048 bytes);
; (2) if it's supposed to be copying EOS-5, it starts in the middle of the
; file I/O routines (address 59392);
; (3) it ends by bank-switching in OS-7/24K RAM for the low 32K.
; The only possible explanations are that
; (1) it's a hook for Super Games, which need EOS block I/O routines,
; but which use OS-7 for video and sound; or
; (2) it's expecting an ADAM with a different version of EOS in ROM.
; EOS-7 is almost but not quite 2K shorter than EOS-5; perhaps it
; was anticipated that it would be shorter in the release version.
;***************************************************************************
;EOS Function 19: READ 1 BLOCK.
; On entry, A=device number, BCDE=block number to read (BC=hiword, DE=
; loword), HL=data transfer address (DTA). On exit, ZF=1 if read was
; successful, ZF=0 if not.
__RD_1_BLK:
PUSH IY
PUSH AF ;save device #
CALL __START_RD_1_BLOCK ;*Fn38: START READ 1 BLOCK
JR NZ,A64173 ;start failed, error exit ZF=0
A64166:
POP AF ;restore device #
PUSH AF ;save it again
CALL __END_RD_1_BLOCK ;*Fn7: END READ 1 BLOCK
JR NC,A64166 ;not done reading, so keep waiting
A64173:
POP IY ;we're done, so pop AF off stack without
;destroying current flags
POP IY
RET
;***************************************************************************
;EOS Function 44: WRITE 1 BLOCK.
; On entry, A=device number, BCDE=block number to write (BC=hiword, DE=
; loword), HL=data transfer address (DTA). On exit, ZF=1 if write was
; successful, ZF=0 and A=error code if not.
__WR_1_BLOCK:
PUSH IY
PUSH AF ;save device #
CALL __START_WR_1_BLOCK ;*Fn41: START WRITE 1 BLOCK
JR NZ,A64193 ;start failed, error exit ZF=0
A64186:
POP AF ;restore device #
PUSH AF ;save it again
CALL __END_WR_1_BLOCK ;*Fn10: END WRITE 1 BLOCK
JR NC,A64186 ;not done writing, so keep waiting
A64193:
POP IY ;we're done, so pop AF off stack without
;destroying current flags
POP IY
RET
;***************************************************************************
;EOS Function 38: START READ 1 BLOCK.
; On entry, A=device number, BCDE=block number to read (BC=hiword, DE=
; loword), HL=data transfer address (DTA). On exit, ZF=1 if start was
; successful, ZF=0 and A=error code (1=NON-EXISTENT DEVICE, 2=DEVICE NOT
; READY) if not.
__START_RD_1_BLOCK:
PUSH BC
PUSH DE
PUSH HL
PUSH IY
CALL __FIND_DCB ;*Fn12/13: FIND/GET DCB ADDRESS (in IY)
JR NZ,A64220 ;device doesn't exist, error exit ZF=0
CALL A62614 ;CHECK IF DEVICE (IN A) IS READY subrt
JR NZ,A64220 ;device not ready, so error exit ZF=0
CALL A64312 ;SET UP DCB FOR I/O OPERATION subrt
LD (IY+0),4 ;request read
A64220:
POP IY
POP HL
POP DE
POP BC
RET
;***************************************************************************
;EOS Function 7: END READ 1 BLOCK.
; On entry, A=device number. On exit, CF=1 if the I/O attempt has ended,
; with ZF=1 for ended successfully, ZF=0 for I/O error. If I/O is still
; in progress, CF=0. Error codes in A may be 1=NON-EXISTENT DEVICE, 3=
; I/O NOT DONE. (Exit from bit 7 clear test has A=0, which usually means
; OK.)
__END_RD_1_BLOCK:
PUSH IY
CALL __FIND_DCB ;*Fn12/13: FIND/GET DCB ADDRESS (in IY)
SCF ;set CF
JR NZ,A64252 ;device doesn't exist, error exit ZF=0
CALL A62638 ;CHECK IF DEVICE I/O IS DONE subroutine
JR NZ,A64252 ;I/O not done, so error exit ZF=0, CF=0
OR A ;I/O done, CF=0 (already clear from subrt)
BIT 7,(IY+0) ;is bit 7 of DCB status byte set?
JR Z,A64252 ;NO, so error exit ZF=1, CF=0
LD A,(IY+0) ;A=status byte
CP 128 ;is it 128? (successfully ended)
SCF ;set CF
A64252:
POP IY
RET
;***************************************************************************
;EOS Function 41: START WRITE 1 BLOCK.
; On entry, A=device number, BCDE=block number to write (BC=hiword, DE=
; loword), HL=data transfer address (DTA). On exit, ZF=1 if start was
; successful, ZF=0 and A=error code if not.
__START_WR_1_BLOCK:
PUSH BC
PUSH DE
PUSH HL
PUSH IY
CALL __FIND_DCB ;*Fn12/13: FIND/GET DCB ADDRESS (in IY)
JR NZ,A64277 ;device doesn't exist, error exit ZF=0
CALL A62614 ;CHECK IF DEVICE (IN A) IS READY subrt
JR NZ,A64277 ;device not ready, so error exit ZF=0
CALL A64312 ;SET UP DCB FOR I/O OPERATION subrt
LD (IY+0),3 ;request write
A64277:
POP IY
POP HL
POP DE
POP BC
RET
;***************************************************************************
;EOS Function 10: END WRITE 1 BLOCK.
; On entry, A=device number. On exit, CF=1 if the I/O attempt has ended,
; with ZF=1 if successful, ZF=0 for I/O error. If I/O is still in progress,
; CF=0. Error codes in A may be 1=NON-EXISTENT DEVICE, 3=I/O NOT DONE.
; (Exit from bit 7 clear test has A=0, which usually means OK.)
__END_WR_1_BLOCK:
PUSH IY
CALL __FIND_DCB ;*Fn12/13: FIND/GET DCB ADDRESS (in IY)
SCF ;set CF
JR NZ,A64309 ;device doesn't exist, so error exit ZF=0
CALL A62638 ;CHECK IF DEVICE I/O IS DONE subroutine
JR NZ,A64309 ;I/O not done, so error exit ZF=0, CF=0
OR A ;I/O done, CF=0 (already cleared by subrt)
BIT 7,(IY+0) ;is bit 7 of DCB status byte set?
JR Z,A64309 ;NO, so error exit ZF=1, CF=0
LD A,(IY+0) ;A=status byte
CP 128 ;is it 128? (successfully ended)
SCF ;set CF
A64309:
POP IY
RET
;***************************************************************************
;SET UP DCB FOR I/O OPERATION subroutine.
; On entry, A=device #, BCDE=block number to access (BC=hiword, DE=loword),
; HL=data transfer address (DTA), IY=DCB address. The transfer buffer
; length (DCB bytes 4-3) is set to the maximum length (bytes 18-17). BCDE
; is copied to bytes 8-5, HL is copied to bytes 2-1. While byte 3 is set
; to the device number, byte 9 contains the upper nibble of the device
; number. This is how one DCB is made to function for both tape 1 (device
; hex 08) and tape 2 (device hex 18). Note, however, that disk 1 and disk
; 2 have separate DCBs.
A64312:
PUSH AF ;save device #
SRL A ;A/2
SRL A ;A/4
SRL A ;A/8
SRL A ;A/16 new A=upper nibble of entry A
LD (IY+9),A ;put it in the DCB
LD A,(IY+17) ;lobyte maximum buffer length...
LD (IY+3),A ;...is lobyte DTA length
LD A,(IY+18) ;hibyte maximum buffer length...
LD (IY+4),A ;...is hibyte DTA length
LD (IY+5),E ;loword,lobyte of block number=E
LD (IY+6),D ;loword,hibyte=D
LD (IY+7),C ;hiword,lobyte=C
LD (IY+8),B ;hiword,hibyte=B
LD (IY+1),L ;loword DTA start=L
LD (IY+2),H ;hiword DTA start=H
POP AF ;restore device #
RET
;***************************************************************************
;UNUSED EOS FUNCTION: READ FROM CHARACTER DEVICE (IN A).
; This routine is not found in the jump table, though whether by intention
; (i.e., it doesn't work) or oversight I don't know. It is the complement
; to Function 45 (write character device (in A)). On entry, A=device
; number, DE=buffer start address, BC=buffer length. On exit, ZF=1 and A=0
; if read successful, ZF=0 and A=error code (1,2,3) if not.
A64356:
LD (DEVICE_ID),A ;device number=A
CALL __START_RD_CH_DEV ;Fn39: START READ CHARACTER DEVICE (in A)
JR NZ,A64372 ;read failed, so EXIT ZF=0 (try RET NZ?)
A64364:
LD A,(DEVICE_ID) ;restore device number in A
CALL __END_RD_CH_DEV ;Fn8: END READ CHARACTER DEVICE (in A)
JR NC,A64364 ;not done yet, so keep reading
A64372:
RET
;***************************************************************************
;EOS Function 45: WRITE CHARACTER DEVICE (in A).
; On entry, A=device number, HL=buffer start address, BC=buffer length.
; On exit, ZF=1 and A=0 if write was successful, ZF=0 and A=error code
; (1,2,3) if not.
__WR_CH_DEV:
LD (DEVICE_ID),A ;device number=A
CALL __START_WR_CH_DEV ;*Fn42: start write character dev (in A)
JR NZ,A64389 ;write failed, so EXIT ZF=0 (try RET NZ?)
A64381:
LD A,(DEVICE_ID) ;restore device number in A
CALL __END_WR_CH_DEV ;*Fn10: END WRITE CHARACTER DEVICE (in A)
JR NC,A64381 ;not done yet, so keep writing
A64389:
RET
;***************************************************************************
;EOS Function 39: START READ CHARACTER DEVICE (in A).
; On entry, A=device number, DE=buffer start address, BC=buffer length.
; On exit, ZF=1 and A=0 if start was OK. Otherwise, ZF=0 and A=error code
; (1,2).
__START_RD_CH_DEV:
PUSH IY
CALL __FIND_DCB ;*Fn12/13: FIND/GET DCB ADDRESS (in IY)
JR NZ,A64418 ;device doesn't exist, error exit ZF=0
CALL A62614 ;CHECK IF DEVICE (IN A) IS READY subrt
JR NZ,A64418 ;device not ready, so error exit ZF=0
LD (IY+1),E ;device ready! now set up DCB
LD (IY+2),D ;buffer start address=DE
LD (IY+3),C
LD (IY+4),B ;buffer length=BC
LD (IY+0),4 ;request read
A64418:
POP IY
RET
;***************************************************************************
;EOS Function 8: END READ CHARACTER DEVICE (in A).
; On entry, A=device number. On exit, CF=1 if the I/O attempt has ended,
; with ZF=1 for ended successfully, ZF=0 for I/O error. If I/O is still
; in progress, CF=0. Error codes in A may be 1=NON-EXISTENT DEVICE, 3=
; I/O NOT DONE. (Exit from bit 7 clear test has A=0, which usually means
; OK.)
__END_RD_CH_DEV:
PUSH IY
CALL __FIND_DCB ;*Fn12/13: FIND/GET DCB ADDRESS (in IY)
SCF ;set CF
JR NZ,A64447 ;device doesn't exist, exit ZF=0, CF=1
CALL A62638 ;CHECK IF DEVICE I/O IS DONE subroutine
JR NZ,A64447 ;not done, so error exit ZF=0, CF=0
OR A ;clear CF (already cleared by DONE subrt)
BIT 7,(IY+0) ;is bit 7 of status byte set?
JR Z,A64447 ;NO, so error exit ZF=1, CF=0
LD A,(IY+0) ;YES, so get status byte in A
CP 128 ;is it 128? (end successful)
SCF ;set CF
A64447:
POP IY
RET
;***************************************************************************
;EOS Function 42: START WRITE CHARACTER DEVICE (in A).
; On entry, A=device number, HL=buffer start address, BC=buffer length.
; On exit, ZF=1 and A=0 if start was OK. Otherwise, ZF=0 and A=error code
; (1,2).
__START_WR_CH_DEV:
PUSH IY
CALL __FIND_DCB ;*Fn12/13: FIND/GET DCB ADDRESS (in IY)
JR NZ,A64478 ;device doesn't exist, so error exit ZF=0
CALL A62614 ;CHECK IF DEVICE (IN A) IS READY subrt
JR NZ,A64478 ;device not ready, so error exit ZF=0
LD (IY+3),C ;device ready! now set up DCB
LD (IY+4),B ;buffer length=BC
LD (IY+1),L
LD (IY+2),H ;buffer start=HL
LD (IY+0),3 ;request write
A64478:
POP IY
RET
;***************************************************************************
;EOS Function 11: END WRITE CHARACTER DEVICE (in A).
; On entry, A=device number. On exit, CF=1 if the I/O attempt has ended,
; with ZF=1 for ended successfully, ZF=0 for I/O error. If I/O is still
; in progress, CF=0. Error codes in A may be 1=NON-EXISTENT DEVICE, 3=
; I/O NOT DONE. (Exit from bit 7 clear test has A=0, which usually means
; OK.)
__END_WR_CH_DEV:
PUSH IY
CALL __FIND_DCB ;*Fn12/13: FIND/GET DCB ADDRESS (in IY)
SCF ;set CF
JR NZ,A64507 ;device doesn't exist, exit ZF=0, CF=1
CALL A62638 ;CHECK IF DEVICE I/O IS DONE subroutine
JR NZ,A64507 ;not done, so error exit ZF=0, CF=0
OR A ;clear CF (already cleared by DONE subrt)
BIT 7,(IY+0) ;is bit 7 of status byte set?
JR Z,A64507 ;NO, so error exit ZF=1, CF=0
LD A,(IY+0) ;get status byte
CP 128 ;is it 128? (end successful)
SCF ;set CF
A64507:
POP IY
RET
;***************************************************************************
;UNUSED DATA?
A64510:
DB 255
;***************************************************************************
EOS_GLB_TBL:
;***************************************************************************
;INTERRUPT VECTOR TABLE.
; This table is used to set up page zero during the powerup boot routine
; found in the SmartWriter ROM.
INT_VCTR_TBL:
VECTOR_08H:
RET ;RST 08H
DW 0
VECTOR_10H:
RET ;RST 10H
DW 0
VECTOR_18H:
RET ;RST 18H
DW 0
VECTOR_20H:
RET ;RST 20H
DW 0
VECTOR_28H:
RET ;RST 28H
DW 0
VECTOR_30H:
RET ;RST 30H
DW 0
VECTOR_38H:
RET ;RST 38H ;shouldn't this be RETI?
DW 0
VECTOR_66H:
RETN ;NMI routine
DB 0
;***************************************************************************
;MEMORY CONFIGURATION TABLE:
; In order to select the SmartWriter ROM, an OUT (63),0 must be executed
; first. To select the EOS ROM, use OUT (63),2.
SWITCH_TABLE:
; lower 32K upper 32K
MEM_CNFG00:
DB 0 ;SmartWriter or EOS RAM
MEM_CNFG01:
DB 1 ;RAM RAM
MEM_CNFG02:
DB 2 ;expansion RAM RAM
MEM_CNFG03:
DB 3 ;OS-7 plus 24K RAM RAM
MEM_CNFG04:
DB 4 ;SmartWriter or EOS expansion ROM
MEM_CNFG05:
DB 5 ;RAM expansion ROM
MEM_CNFG06:
DB 6 ;expansion RAM expansion ROM
MEM_CNFG07:
DB 7 ;OS-7 plus 24K RAM expansion ROM
MEM_CNFG08:
DB 8 ;SmartWriter or EOS expansion RAM
MEM_CNFG09:
DB 9 ;RAM expansion RAM
MEM_CNFG0A:
DB 10 ;expansion RAM expansion RAM
MEM_CNFG0B:
DB 11 ;OS-7 plus 24K RAM expansion RAM
MEM_CNFG0C:
DB 12 ;SmartWriter or EOS cartridge ROM
MEM_CNFG0D:
DB 13 ;RAM cartridge ROM
MEM_CNFG0E:
DB 14 ;expansion RAM cartridge ROM
MEM_CNFG0F:
DB 15 ;OS-7 plus 24K RAM cartridge ROM
;***************************************************************************
;I/O PORTS.
PORT_TABLE:
MEM_SWITCH_PORT:
DB 7FH ;memory switch port (127)
NET_RESET_PORT:
DB 3FH ;ADAMnet reset port (63)
VDP_CTRL_PORT:
DB 0BFH ;VDP control port (191)
VDP_DATA_PORT:
DB 0BEH ;VDP data port (190)
CONTROLLER_0_PORT:
DB 0FCH ;game controller 1 port (252)
CONTROLLER_1_PORT:
DB 0FFH ;game controller 2 port (255)
STROBE_SET_PORT:
DB 80H ;strobe set port (128)
STROBE_RESET_PORT:
DB 0C0H ;strobe reset port (192)
SOUNDPORT:
DB 0FFH ;sound port (255)
;***************************************************************************
;EOS JUMP TABLE: EOS FUNCTIONS 0-100.
; Function names (with modification) from "The Hacker's Guide to ADAM
; Volume I" by Peter and Ben Hinkle (1986).
; Function labels from Coleco's EOS6 source code.
EOS_JMP_TBL:
_EOS_START:
JP __EOS_START ;0 EOS START/INITIALIZATION
_CONS_DISP:
JP __CONS_DISP ;1 CONSOLE DISPLAY OF NON-CONTROL CHAR (in A)
_CONS_INIT:
JP __CONS_INIT ;2 CONSOLE INITIALIZATION
_CONS_OUT:
JP __CONS_OUT ;3 DISPLAY CHAR (in A) ON SCREEN (CTRL OR NOT)
_DLY_AFT_HRD_RES:
JP __DLY_AFT_HRD_RES ;4 DELAY AFTER HARD RESET
_END_PR_BUFF:
JP __END_PR_BUFF ;5 END PRINT BUFFER (at HL)
_END_PR_CH:
JP __END_PR_CH ;6 END PRINT CHARACTER (in A)
_END_RD_1_BLOCK:
JP __END_RD_1_BLOCK ;7 END READ 1 BLOCK
_END_RD_CH_DEV:
JP __END_RD_CH_DEV ;8 END READ CHARACTER DEVICE (in A)
_END_RD_KBD:
JP __END_RD_KBD ;9 END READ KEYBOARD
_END_WR_1_BLOCK:
JP __END_WR_1_BLOCK ;10 END WRITE 1 BLOCK
_END_WR_CH_DEV:
JP __END_WR_CH_DEV ;11 END WRITE CHARACTER DEVICE (in A)
_FIND_DCB:
JP __FIND_DCB ;12 FIND DCB
_GET_DCB_ADDR:
JP __GET_DCB_ADDR ;13 GET DCB ADDRESS (in IY)
_GET_PCB_ADDR:
JP __GET_PCB_ADDR ;14 GET PCB ADDRESS (in IY)
_HARD_INIT:
JP __HARD_INIT ;15 HARD INITIALIZATION (COLD BOOT)
_HARD_RESET_NET:
JP __HARD_RESET_NET ;16 HARD RESET ADAMnet
_PR_BUFF:
JP __PR_BUFF ;17 PRINT BUFFER (at HL)
_PR_CH:
JP __PR_CH ;18 PRINT CHARACTER (in A)
_RD_1_BLOCK:
JP __RD_1_BLK ;19 READ 1 BLOCK
_RD_KBD:
JP __RD_KBD ;20 READ KEYBOARD
_RD_KBD_RET_CODE:
JP __RD_KBD_RET_CODE ;21 READ KEYBOARD STATUS BYTE
_RD_PR_RET_CODE:
JP __RD_PR_RET_CODE ;22 READ PRINTER STATUS BYTE
_RD_RET_CODE:
JP __RD_RET_CODE ;23 READ DEVICE (in A) STATUS BYTE
_RD_TAPE_RET_CODE:
JP __RD_TAPE_RET_CODE ;24 READ TAPE STATUS BYTE
_RELOC_PCB:
JP __RELOC_PCB ;25 RELOCATE PCB (to HL)
_REQUEST_STATUS:
JP __REQUEST_STATUS ;26 REQUEST DEVICE (in A) STATUS
_REQ_KBD_STAT:
JP __REQ_KBD_STAT ;27 REQUEST KEYBOARD STATUS
_REQ_PR_STAT:
JP __REQ_PR_STAT ;28 REQUEST PRINTER STATUS
_REQ_TAPE_STAT:
JP __REQ_TAPE_STAT ;29 REQUEST TAPE STATUS
_SCAN_ACTIVE:
JP __SCAN_ACTIVE ;30 SCAN ADAMnet FOR DEVICES
_SOFT_INIT:
JP __SOFT_INIT ;31 SOFT INITIALIZATION (WARM BOOT)
_SOFT_RES_DEV:
JP __SOFT_RES_DEV ;32 SOFT RESET DEVICE (in A)
_SOFT_RES_KBD:
JP __SOFT_RES_KBD ;33 SOFT RESET KEYBOARD
_SOFT_RES_PR:
JP __SOFT_RES_PR ;34 SOFT RESET PRINTER
_SOFT_RES_TAPE:
JP __SOFT_RES_TAPE ;35 SOFT RESET TAPE
_START_PR_BUFF:
JP __START_PR_BUFF ;36 START PRINT BUFFER (at HL)
_START_PR_CH:
JP __START_PR_CH ;37 START PRINT CHARACTER (in A)
_START_RD_1_BLOCK:
JP __START_RD_1_BLOCK ;38 START READ 1 BLOCK
_START_RD_CH_DEV:
JP __START_RD_CH_DEV ;39 START READ CHARACTER DEVICE (in A)
_START_RD_KBD:
JP __START_RD_KBD ;40 START READ KEYBOARD
_START_WR_1_BLOCK:
JP __START_WR_1_BLOCK ;41 START WRITE 1 BLOCK
_START_WR_CH_DEV:
JP __START_WR_CH_DEV ;42 START WRITE CHARACTER DEVICE (in A)
_SYNC:
JP __SYNC ;43 SYNCHRONIZE Z80A AND MASTER 6801 CLOCKS
_WR_1_BLOCK:
JP __WR_1_BLOCK ;44 WRITE 1 BLOCK
_WR_CH_DEV:
JP __WR_CH_DEV ;45 WRITE CHARACTER DEVICE (in A)
_FMGR_INIT:
JP __FMGR_INIT ;46 INITIALIZE FILE MANAGER
_INIT_TAPE_DIR:
JP __INIT_TAPE_DIR ;47 INITIALIZE DIRECTORY
_OPEN_FILE:
JP __OPEN_FILE ;48 OPEN FILE
_CLOSE_FILE:
JP __CLOSE_FILE ;49 CLOSE FILE
_RESET_FILE:
JP __RESET_FILE ;50 RESET FILE
_MAKE_FILE:
JP __MAKE_FILE ;51 CREATE FILE
_QUERY_FILE:
JP __QUERY_FILE ;52 FIND FILE (WITH TYPE)
_SET_FILE:
JP __SET_FILE ;53 UPDATE DIRECTORY ENTRY
_READ_FILE:
JP __READ_FILE ;54 READ FILE
_WRITE_FILE:
JP __WRITE_FILE ;55 WRITE FILE
_SET_DATE:
JP __SET_DATE ;56 SET CURRENT DATE
_GET_DATE:
JP __GET_DATE ;57 GET CURRENT DATE
_RENAME_FILE:
JP __RENAME_FILE ;58 RENAME FILE
_DELETE_FILE:
JP __DELETE_FILE ;59 DELETE FILE
_RD_DEV_DEP_STAT:
JP __RD_DEV_DEP_STAT ;60 READ DEVICE (in A) NODE TYPE
_GOTO_WP:
JP __GOTO_WP ;61 GO TO SmartWriter
_READ_EOS:
JP __READ_EOS ;62 READ EOS [not implemented in EOS-5]
_TRIM_FILE:
JP __TRIM_FILE ;63 TRIM FILE
_CHECK_FCB:
JP __CHECK_FCB ;64 CHECK IF FILE IS OPEN
_READ_BLOCK:
JP __READ_BLOCK ;65 READ BLOCK
_WRITE_BLOCK:
JP __WRITE_BLOCK ;66 WRITE BLOCK
_MODE_CHECK:
JP __MODE_CHECK ;67 CHECK FILE I/O MODE
_SCAN_FOR_FILE:
JP __SCAN_FOR_FILE ;68 READ DIRECTORY FOR FILE
_FILE_QUERY:
JP __FILE_QUERY ;69 FIND FILE (NO TYPE)
_POSIT_FILE:
JP __POSIT_FILE ;70 POSITION FILE [not implemented in EOS-5]
_EOS_1:
JP __EOS_1 ;71 EOS1 [not implemented in EOS-5]
_EOS_2:
JP __EOS_2 ;72 EOS2 [not implemented in EOS-5]
_EOS_3:
JP __EOS_3 ;73 EOS3 [not implemented in EOS-5]
_CV_A:
JP __CV_A ;74 INCORRECT EOS VERSION ERROR
PORT_COLLECTION:
JP __PORT_COLLECTION ;75 GET I/O PORTS FROM OS-7
SWITCH_MEM:
JP __SWITCH_MEM ;76 BANK SWITCH MEMORY (to A)
PUT_ASCII:
JP __PUT_ASCII ;77 PUT ASCII CHARACTER PATTERN TO VDP
WRITE_VRAM:
JP __WRITE_VRAM ;78 WRITE VRAM
READ_VRAM:
JP __READ_VRAM ;79 READ VRAM
WRITE_REGISTER:
JP __WRITE_REGISTER ;80 WRITE VDP REGISTER 0-7
READ_REGISTER:
JP __READ_REGISTER ;81 READ VDP REGISTER 8
FILL_VRAM:
JP __FILL_VRAM ;82 FILL VRAM WITH 1 CHARACTER (in A)
INIT_TABLE:
JP __INIT_TABLE ;83 INITIALIZE VRAM TABLE
PUT_VRAM:
JP __PUT_VRAM ;84 PUT TABLE TO VRAM
GET_VRAM:
JP __GET_VRAM ;85 GET TABLE FROM VRAM
CALC_OFFSET:
JP __CALC_OFFSET ;86 CALCULATE OFFSET INTO SPRITE ATTRIB TABLE
PX_TO_PTRN_POS:
JP __PX_TO_PTRN_POS ;87 POINT TO PATTERN POSITION
LOAD_ASCII:
JP __LOAD_ASCII ;88 LOAD ASCII CHARACTER SET FROM ROM TO VDP
WR_SPR_ATTRIBUTE:
JP __WR_SPR_ATTRIBUTE ;89 WRITE VRAM SPRITE ATTRIBUTE TABLE
POLLER:
JP __POLLER ;90 READ GAME CONTROLLERS
UPDATE_SPINNER:
JP __UPDATE_SPINNER ;91 UPDATE SPINNER 1 AND 2
DECLSN:
JP __DECLSN ;92 DECREMENT LOW NIBBLE OF (HL)
DECMSN:
JP __DECMSN ;93 DECREMENT HIGH NIBBLE OF (HL)
MSNTOLSN:
JP __MSNTOLSN ;94 HIGH NIBBLE OF (HL) TO LOW NIBBLE
ADD816:
JP __ADD816 ;95 ADD A TO WORD AT HL
SOUND_INIT:
JP __SOUND_INIT ;96 SOUND INITIALIZATION
TURN_OFF_SOUND:
JP __TURN_OFF_SOUND ;97 SOUND OFF
PLAY_IT:
JP __PLAY_IT ;98 START VOICE
SOUNDS:
JP __SOUNDS ;99 SOUND
EFFECT_OVER:
JP __EFFECT_OVER ;100 END SPECIAL EFFECT NOTE
;***************************************************************************
;UNUSED DATA?
DB 255
;***************************************************************************
EOS_GLB_RAM:
CLEAR_RAM_START:
;***************************************************************************
;VIDEO AND MEMORY DATA.
REV_NUM:
DS 1 ;EOS revision number
VDP_MODE_WORD:
VDP_REG_0:
DS 1 ;current contents of VDP register 0
VDP_REG_1:
DS 1 ;current contents of VDP register 1
VDP_STATUS_BYTE:
DS 1 ;VDP status byte
VRAM_ADDR_TABLE:
SPRITEATTRTBL:
DS 2 ;VRAM address of sprite attribute table
SPRITEGENTBL:
DS 2 ;VRAM address of sprite generator table
PATTRNNAMETBL:
DS 2 ;VRAM address of pattern name table
PATTRNGENTBL:
DS 2 ;VRAM address of pattern generator table
COLORTABLE:
DS 2 ;VRAM address of color table
CUR_BANK:
DS 1 ;current memory configuration (from last bank switch)
;***************************************************************************
;FILE CONTROL DATA.
DEFAULT_BT_DEV:
CURRENT_DEV:
DS 1 ;current device number
CURRENT_PCB:
DS 2 ;address of current PCB
DEVICE_ID:
DS 1 ;device number
FILE_NAME_ADDR:
DS 2 ;address of filename string
KEYBOARD_BUFFER:
DS 1 ;keyboard buffer (last key pressed)
PRINT_BUFFER:
DS 16 ;printer buffer
SECTORS_TO_INIT:
DS 1 ;number of blocks to initialize
SECTOR_NO:
DS 4 ;block to initialize
DCB_IMAGE:
DS 21 ;DCB image [not used in EOS-5]
QUERY_BUFFER:
DS 26 ;query buffer
FCB_BUFFER:
DS 26 ;FCB buffer
FILE_COUNT:
DS 1 ;file count
MOD_FILE_COUNT:
DS 1 ;mod file count
RETRY_COUNT:
DS 1 ;retry count
FILE_NUMBR:
DS 1 ;file number
FILENAME_CMPS:
DS 1 ;file name comparison status byte
;(0=name and file type must match, anything
;else=only name must match)
DIR_BLOCK_NO:
DS 2 ;directory block number
FOUND_AVAIL_ENT:
DS 1 ;found entry status byte
;(1=matching entry found, 0 if not)
BLK_STRT_PTR:
VOL_BLK_SZ:
DS 4 ;volume block size [not used in EOS-5]
EOS_YEAR:
DS 1 ;current file creation year
EOS_MONTH:
DS 1 ;current file creation month
EOS_DAY:
DS 1 ;current file creation day
FMGR_DIR_ENT:
DS 26 ;file manager directory entry [not used in EOS-5]
FCB_HEAD_ADDR:
DS 2 ;address of FCB0 (FCB1 offset 35, FCB2 offset 70)
FCB_DATA_ADDR:
DS 2 ;address of DTA0 (DTA1 offset 1024, DTA2 offset 2048)
FNUM:
DS 1 ;file number (01 or 02)
BYTES_REQ:
DS 2 ;bytes requested to read/write
BYTES_TO_GO:
DS 2 ;bytes left to read/write
USER_BUF:
DS 2 ;read/write buffer address
BUF_START:
DS 2 ;buffer start address
BUF_END:
DS 2 ;buffer end address
BLOCKS_REQ:
DS 4 ;file length in blocks
USER_NAME:
DS 2 ;filename string address
START_BLOCK:
DS 4 ;start block [not used in EOS-5]
NEW_HOLE_START:
DS 4 ;new hole start block
NEW_HOLE_SIZE:
DS 2 ;new hole size in blocks
;***************************************************************************
;EOS STACK.
STACK_START:
DS 60 ;bottom to top of EOS stack
EOS_STACK:
;***************************************************************************
;GAME CONTROLLER DATA.
; This seemingly illogical setup was verified by examining SmartBASIC's
; PDL routines. "The Hacker's Guide to ADAM Volume I" is incorrect on
; this point--they are clearly following the ColecoVision Technical
; Manual here. The game controller data structures are different between
; EOS and OS-7.
SPIN_SW0_CT:
DS 1 ;spinner player 1
SPIN_SW1_CT:
DS 1 ;spinner player 2
PERSONAL_DEBOUNCE_TABLE:
DS 1 ;joystick player 2
DS 1 ;left button player 2
DS 1 ;right button player 2
DS 1 ;keypad player 2
DS 1 ;joystick player 1
DS 1 ;left button player 1
DS 1 ;right button player 1
DS 1 ;keypad player 1
;***************************************************************************
;TEMPORARY STACK USED BY FUNCTION 77: PUT ASCII CHARACTER TO VDP.
DS 12 ;bottom to top -- at maximum usage, stack contains:
;65132-33 old stack pointer
;65130-31 old memory configuration and flags
;65128-29 return address 57726 after call to Fn78
;65126-27 BC register saved by Fn78
;65124-25 return addr 57349 after call to ENABLE VRAM WRITE
;65122-23 0000 (unused)
TEMP_STACK:
;***************************************************************************
;SOUND DATA.
PTR_TO_LST_OF_SND_ADDRS:
DS 2 ;address of voice table
PTR_TO_S_ON_0:
DS 2 ;address of noise output table
PTR_TO_S_ON_1:
DS 2 ;address of voice 1 output table
PTR_TO_S_ON_2:
DS 2 ;address of voice 2 output table
PTR_TO_S_ON_3:
DS 2 ;address of voice 3 output table
SAVE_CTRL:
DS 1 ;saved control sound
;***************************************************************************
;VIDEO DISPLAY DATA.
; Note: SmartBASIC does not use this space, since it does not call EOS
; to display characters.
OLDCHAR_:
DS 1 ;old character (at current cursor position)
X_MIN:
DS 1 ;X min (column)
X_MAX:
DS 1 ;X max (column)
Y_MIN:
DS 1 ;Y min (line)
Y_MAX:
DS 1 ;Y max (line)
LINEBUFFER_:
DS 33 ;line buffer
NUM_LINES:
DS 1 ;number of lines
NUM_COLUMNS:
DS 1 ;number of columns
UPPER_LEFT:
DS 1 ;screen upper left corner line (Y)
DS 1 ;screen upper left corner column (X)
PTRN_NAME_TBL:
DS 2 ;address of name table
CURSOR:
DS 1 ;current cursor position line (Y)
DS 1 ;current cursor column (X)
;***************************************************************************
;DATA UNUSED IN EOS-5.
; This space was reserved for future versions of EOS. Some of it is used
; in EOS-7.
DB 0
DS 24
;***************************************************************************
EOS_PCB_DCB:
;***************************************************************************
;PROCESSOR CONTROL BLOCK (PCB).
; The PCB is a 4-byte block, usually at address 65216 but relocatable, used
; by the master 6801 ADAMnet controller to keep track of devices.
PCB:
DS 1 ;processor status byte
DS 2 ;address of PCB start (65216)
DS 1 ;number of active devices (number of DCBs)
; Reading byte 0 returns status information from ADAMnet; the meaning of
; individual status bits is uncertain. Writing to byte 0 requests the
; following operations:
;data function
; 1 synchronize the Z80 clock
; 2 synchronize the master 6801 clock
; 3 relocate PCB
;***************************************************************************
;DEVICE CONTROL BLOCKS (DCBs).
; Each DCB is 21 bytes long, with a maximum of 15 devices, in ascending
; order of primary device number. (Device numbers may be greater than 15,
; however. See tape DCB.) The number of DCBs depends upon how many valid
; devices were attached to ADAMnet at startup.
;offset meaning
; 0 status byte
; 1-2 buffer start address (lobyte, hibyte)
; 3-4 buffer length (lobyte, hibyte)
; 5-8 block number accessed (loword, hiword in lobyte, hibyte format)
; 9 high nibble of device number
;10-15 always zero (unknown purpose)
; 16 device number
;17-18 maximum block length (lobyte, hibyte)
; 19 device type (0 for block device, 1 for character device)
; 20 node type (see Function 60 for details)
; Reading byte 0 returns status information from ADAMnet; the meaning of
; individual status bits is uncertain. Empirically, anything with bit 7
; clear means that nothing has happened yet (the command is still being
; processed); 80h means success, 9Bh means timeout (i.e. there is no
; ADAMnet device corresponding to the device number you were trying to
; access), anything else is some kind of error or not-ready condition.
; Writing to byte 0 requests the following operations:
;data function
; 1 return current status
; 2 soft reset
; 3 write
; 4 read
; ADAMnet device numbers decode as follows:
;number device type
; 0 master 6801 ADAMnet controller -
; 1 keyboard 1
; 2 ADAM printer 1
; 3 Copywriter 1 ??
; 4 disk drive 1 0
; 5 disk drive 2 0
; 6 disk drive 3 0
; 7 disk drive 4 0
; 8 tape drive 1 0
; 9 tape drive 3 0
; 10 -- unused -- -
; 11 Non-ADAMlink modem 1 ??
; 12 Hi-resolution monitor 1 ??
; 13 ADAM parallel interface 1
; 14 ADAM serial interface 1
; 15 Gateway ???
; 24 tape drive 2 0
; 25 tape drive 4 0
; 26 expansion RAM disk drive -
;Device Notes:
; 0 The master 6801 uses the PCB as its DCB.
; 3 Projected dot matrix printer?
; 6,7 Third-party modified disk drive EPROMs to change ADAMnet device
; ID.
; 8,24 Tape 1 and tape 2 share the same DCB.
; 9,25 Projected. Tape3 and tape 4 would have shared the same DCB.
; 11,12 Projected but probably never designed or built.
; 13 The prototype ADAM parallel interface, never released.
; SmartBASIC 2.0 has routines to access it as PR #4.
; 14 The prototype ADAM serial interface, never released. Smart-
; BASIC 2.0 has routines to access it as IN #2 and PR #2.
; 15 This always appears as the last DCB, and always with a not-
; ready status (see __SCAN_ACTIVE code). Presumably would have
; been used to network ADAMs via ADAMnet.
; 26 Used as a third-party RAMdisk device ID. Not used by Coleco.
; Probably derived from an error in interpreting SmartBASIC 1.0's
; drive-to-device table. 1A hex is the first byte of code after
; the table, which only has entries for D1-D6. The fetch routine,
; however, accepts D7, so it returns code as data.
;***************************************************************************
DCBS:
DS 15*21 ;DCBs
;***************************************************************************
RESERVED_BYTE:
DS 1 ;reserved byte for "fast DMA" (unimplemented)
;This is the end of EOS (address 65535, 0FFFFh).
;***************************************************************************
;EOS DIRECTORY STRUCTURE.
; Each entry consists of 26 bytes. Directories begin at block 1, and may
;be up to 255 blocks long. Entries for files have the following format:
;bytes 0-11 Filename specification. A valid filename consists of up to 10
; characters, followed by a type byte, terminated with hex 03.
; The type byte can be A, a, H, or h (ASCII or hex, lowercase for
; backups).
; 12 File attribute. Set bits in the attribute byte map as follows:
; 0 not a file (used for BLOCKS LEFT)
; 1 execute protected (can't be opened for execution)
; 2 deleted file
; 3 system file (hidden from SmartBASIC CATALOG)
; 4 user file
; 5 read-protected
; 6 write-protected (read only)
; 7 delete protected
; 13-16 Start block. Stored loword, hiword.
; 17-18 Allocated length. Includes any unused "holes" which may be
; tacked on at the end.
; 19-20 Used length. Actual length of file, discounting unused "holes"
; at the end.
; 21-22 Number of bytes used in last allocated block of file. Thus, to
; compute file length in bytes, (allocated-used-1)*1024+lastbyte.
; 23 Creation year. There is no set rule for how to represent the
; year. One common method is (year-1900); thus 1987 would be hex
; 57. This wastes the values hex 00-53 (since ADAM appeared in
; 1983). I propose that these be interpreted as (year+2000).
; 24 Creation month. Range hex 01-0C.
; 25 Creation day. Range hex 01-1F.
; Four special entries are found in the directory of every EOS disk. The
;first three are always:
; VOLUME. bytes 0-11 volume name
; 12 attribute=hex 80 (delete protected)
; 13-16 hex 55AA00FF directory check for EOS format
; 17-18 disk size in blocks
; BOOT. bytes 0-11 BOOT
; 12 attribute=hex 88 (delete protected, system
; file)
; 17-18 allocated length=1 block
; 19-20 used length=1 block
; 21-22 lastbyte=0 (means 1024, i.e. the whole block)
; DIRECTORY. bytes 0-11 DIRECTORY
; 12 attribute=hex C8 (delete and write pretected,
; system file)
; 13-16 start block (default=1)
; 17-18 maximum size of directory in blocks
; 19-20 current size of directory in blocks
; The last entry of every directory in EOS-5 is:
; BLOCKS LEFT. bytes 0-11 BLOCKS LEFT
; 12 attribute=hex 01 (not a file)
; 13-16 first free block in largest contiguous
; cluster of free blocks at the end of the
; storage medium
; 17-18 total number of free blocks (contiguous or
; not)
; 19-20 used length always 0
; 23-25 EOS version date=hex 570711
; This date does not conform to the pattern
; described above. In "The Hacker's Guide To
; ADAM Vol. II", Ben Hinkle suggests that this
; should be read as 7/11/1957, perhaps the
; birthdate of one of the programmers.
; Note: EOS-7 does not use BLOCKS LEFT to keep track of free blocks.
;***************************************************************************
;EOS FILE CONTROL BLOCK (FCB) STRUCTURE.
; EOS sets up areas of RAM to store data about open files, called file
;control blocks (FCBs). EOS-5 has 3 FCBs: 1 for the system, and 2 for user
;files (3 in EOS-7). FCBs are contiguous 35-byte blocks set up as follows:
; bytes 0-22 from EOS directory entry (no date bytes)
; 23 EOS device # using the FCB
; 24 I/O mode byte. The set bits are mapped as follows:
; 7 current block in buffer is the last block
; of the file
; 6 data in buffer is waiting to be written
; to the file. Makes EOS write the data
; before loading a new block into the buffer.
; 5 file won't reuse deleted file space
; 4 unused
; 3 unused
; 2 open for execute
; 1 open for write
; 0 open for read
; Bits 5,2,1,0 are set by the caller; bits 7,6 are set
; and used internally by EOS. Files can be opened for
; read, write, read-write or execute; an error results
; if read, write and execute are simultaneously set.
; 25-28 current block for I/O. Loword, hiword.
; 29-32 last block of file. Loword, hiword.
; 33-34 address of I/O buffer (DTA). This area is also used in
; EOS-7 by Fn70 (POSITION FILE) and Fn50 (RESET FILE) to
; store the byte offset into the current DTA.
;
;******************************************************************************
;EOS ERROR CODES.
; These are returned in A with ZF=0 upon return after an error. I have
;selected likely names based upon what seems to be going wrong when they are
;issued. In some cases, I have provided alternate names which may be more
;meaningful or explanatory. The recently-obtained "ADAM Technical Manual"
;(Coleco, 1984) lists the actual assembly symbols used for these errors.
; code meaning Coleco symbol
; [0 No Error] ---
; 1 Non-Existent Device DCB_NOT_FOUND
; 2 Device Not Ready DCB_BUSY
; 3 I/O Not Done (I/O Not Over) DCB_IDLE_ERR
; 4 No Date Set NO_DATE_ERR
; 5 No More Directory (File Not Found) [Fn68] NO_FILE_ERR
; File Not Open [Fn64]
; 6 File Already Exists FILE_EXISTS_ERR
; 7 Too Many Open Files NO_FCB_ERR
; 8 Match Not Found MATCH_ERR
; 9 Bad File Number BAD_FNUM_ERR
; 10 I/O Past End EOF_ERR
; 11 File Too Big TOO_BIG_ERR
; 12 Directory Full FULL_DIR_ERR
; 13 No More Room (Disk Full) FULL_TAPE_ERR
; 14 Bad File Name FILE_NM_ERR
; 15 [unused; see error 5] RENAME_ERR
; 16 File Locked (File Write Protected) DELETE_ERR
; 17 Bad I/O Mode RANGE_ERR
; 18 Synch 1 Failed CANT_SYNC1
; 19 Synch 2 Failed CANT_SYNC2
; 20 File Access Denied PRT_ERR
; 21 [unknown, unimplemented] RQ_TP_STAT_ERR
; 22 I/O Error DEVICE_DEPD_ERR
; 23 Incorrect EOS Version PROG_NON_EXIST
; 24 Non-EOS Volume NO_DIR_ERR
;Error notes:
; 5,15 Due to a possible typographical error by a programmer, two
; different errors are allocated to 5, while 15 is unused.
; Since SmartBASIC treats 5 as "File Not Found", this suggests
; that "File Not Open" should have been 15.
; 7 SmartBASIC calls this "No Buffers Available".
; 12,13 SmartBASIC does not differentiate between these, lumping them
; both together as "No More Room".
; 20 PRT_ERR means "Protected Error".
; 21 RQ_TP_STAT_ERR means "Request Tape Status Error". It appears
; only in an orphaned code fragment.
; 22 DEVICE_DEPD_ERR means "Device-Dependent Error".
;***************************************************************************